aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/botocore/session.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/botocore/session.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/botocore/session.py')
-rw-r--r--.venv/lib/python3.12/site-packages/botocore/session.py1299
1 files changed, 1299 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/botocore/session.py b/.venv/lib/python3.12/site-packages/botocore/session.py
new file mode 100644
index 00000000..740db2fd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/botocore/session.py
@@ -0,0 +1,1299 @@
+# Copyright (c) 2012-2013 Mitch Garnaat http://garnaat.org/
+# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+"""
+This module contains the main interface to the botocore package, the
+Session object.
+"""
+
+import copy
+import logging
+import os
+import platform
+import socket
+import warnings
+
+import botocore.client
+import botocore.configloader
+import botocore.credentials
+import botocore.tokens
+from botocore import (
+ UNSIGNED,
+ __version__,
+ handlers,
+ invoke_initializers,
+ monitoring,
+ paginate,
+ retryhandler,
+ translate,
+ waiter,
+)
+from botocore.compat import HAS_CRT, MutableMapping
+from botocore.configprovider import (
+ BOTOCORE_DEFAUT_SESSION_VARIABLES,
+ ConfigChainFactory,
+ ConfiguredEndpointProvider,
+ ConfigValueStore,
+ DefaultConfigResolver,
+ SmartDefaultsConfigStoreFactory,
+ create_botocore_default_config_mapping,
+)
+from botocore.context import get_context, with_current_context
+from botocore.errorfactory import ClientExceptionsFactory
+from botocore.exceptions import (
+ ConfigNotFound,
+ InvalidDefaultsMode,
+ PartialCredentialsError,
+ ProfileNotFound,
+ UnknownServiceError,
+)
+from botocore.hooks import (
+ EventAliaser,
+ HierarchicalEmitter,
+ first_non_none_response,
+)
+from botocore.loaders import create_loader
+from botocore.model import ServiceModel
+from botocore.parsers import ResponseParserFactory
+from botocore.regions import EndpointResolver
+from botocore.useragent import UserAgentString
+from botocore.utils import (
+ EVENT_ALIASES,
+ IMDSRegionProvider,
+ validate_region_name,
+)
+
+from botocore.compat import HAS_CRT # noqa
+
+
+logger = logging.getLogger(__name__)
+
+
+class Session:
+ """
+ The Session object collects together useful functionality
+ from `botocore` as well as important data such as configuration
+ information and credentials into a single, easy-to-use object.
+
+ :ivar available_profiles: A list of profiles defined in the config
+ file associated with this session.
+ :ivar profile: The current profile.
+ """
+
+ SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES)
+
+ #: The default format string to use when configuring the botocore logger.
+ LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+
+ def __init__(
+ self,
+ session_vars=None,
+ event_hooks=None,
+ include_builtin_handlers=True,
+ profile=None,
+ ):
+ """
+ Create a new Session object.
+
+ :type session_vars: dict
+ :param session_vars: A dictionary that is used to override some or all
+ of the environment variables associated with this session. The
+ key/value pairs defined in this dictionary will override the
+ corresponding variables defined in ``SESSION_VARIABLES``.
+
+ :type event_hooks: BaseEventHooks
+ :param event_hooks: The event hooks object to use. If one is not
+ provided, an event hooks object will be automatically created
+ for you.
+
+ :type include_builtin_handlers: bool
+ :param include_builtin_handlers: Indicates whether or not to
+ automatically register builtin handlers.
+
+ :type profile: str
+ :param profile: The name of the profile to use for this
+ session. Note that the profile can only be set when
+ the session is created.
+
+ """
+ if event_hooks is None:
+ self._original_handler = HierarchicalEmitter()
+ else:
+ self._original_handler = event_hooks
+ self._events = EventAliaser(self._original_handler)
+ if include_builtin_handlers:
+ self._register_builtin_handlers(self._events)
+ self.user_agent_name = 'Botocore'
+ self.user_agent_version = __version__
+ self.user_agent_extra = ''
+ # The _profile attribute is just used to cache the value
+ # of the current profile to avoid going through the normal
+ # config lookup process each access time.
+ self._profile = None
+ self._config = None
+ self._credentials = None
+ self._auth_token = None
+ self._profile_map = None
+ # This is a dict that stores per session specific config variable
+ # overrides via set_config_variable().
+ self._session_instance_vars = {}
+ if profile is not None:
+ self._session_instance_vars['profile'] = profile
+ self._client_config = None
+ self._last_client_region_used = None
+ self._components = ComponentLocator()
+ self._internal_components = ComponentLocator()
+ self._register_components()
+ self.session_var_map = SessionVarDict(self, self.SESSION_VARIABLES)
+ if session_vars is not None:
+ self.session_var_map.update(session_vars)
+ invoke_initializers(self)
+
+ def _register_components(self):
+ self._register_credential_provider()
+ self._register_token_provider()
+ self._register_data_loader()
+ self._register_endpoint_resolver()
+ self._register_event_emitter()
+ self._register_response_parser_factory()
+ self._register_exceptions_factory()
+ self._register_config_store()
+ self._register_monitor()
+ self._register_default_config_resolver()
+ self._register_smart_defaults_factory()
+ self._register_user_agent_creator()
+
+ def _register_event_emitter(self):
+ self._components.register_component('event_emitter', self._events)
+
+ def _register_token_provider(self):
+ self._components.lazy_register_component(
+ 'token_provider', self._create_token_resolver
+ )
+
+ def _create_token_resolver(self):
+ return botocore.tokens.create_token_resolver(self)
+
+ def _register_credential_provider(self):
+ self._components.lazy_register_component(
+ 'credential_provider', self._create_credential_resolver
+ )
+
+ def _create_credential_resolver(self):
+ return botocore.credentials.create_credential_resolver(
+ self, region_name=self._last_client_region_used
+ )
+
+ def _register_data_loader(self):
+ self._components.lazy_register_component(
+ 'data_loader',
+ lambda: create_loader(self.get_config_variable('data_path')),
+ )
+
+ def _register_endpoint_resolver(self):
+ def create_default_resolver():
+ loader = self.get_component('data_loader')
+ endpoints, path = loader.load_data_with_path('endpoints')
+ uses_builtin = loader.is_builtin_path(path)
+ return EndpointResolver(endpoints, uses_builtin_data=uses_builtin)
+
+ self._internal_components.lazy_register_component(
+ 'endpoint_resolver', create_default_resolver
+ )
+
+ def _register_default_config_resolver(self):
+ def create_default_config_resolver():
+ loader = self.get_component('data_loader')
+ defaults = loader.load_data('sdk-default-configuration')
+ return DefaultConfigResolver(defaults)
+
+ self._internal_components.lazy_register_component(
+ 'default_config_resolver', create_default_config_resolver
+ )
+
+ def _register_smart_defaults_factory(self):
+ def create_smart_defaults_factory():
+ default_config_resolver = self._get_internal_component(
+ 'default_config_resolver'
+ )
+ imds_region_provider = IMDSRegionProvider(session=self)
+ return SmartDefaultsConfigStoreFactory(
+ default_config_resolver, imds_region_provider
+ )
+
+ self._internal_components.lazy_register_component(
+ 'smart_defaults_factory', create_smart_defaults_factory
+ )
+
+ def _register_response_parser_factory(self):
+ self._components.register_component(
+ 'response_parser_factory', ResponseParserFactory()
+ )
+
+ def _register_exceptions_factory(self):
+ self._internal_components.register_component(
+ 'exceptions_factory', ClientExceptionsFactory()
+ )
+
+ def _register_builtin_handlers(self, events):
+ for spec in handlers.BUILTIN_HANDLERS:
+ if len(spec) == 2:
+ event_name, handler = spec
+ self.register(event_name, handler)
+ else:
+ event_name, handler, register_type = spec
+ if register_type is handlers.REGISTER_FIRST:
+ self._events.register_first(event_name, handler)
+ elif register_type is handlers.REGISTER_LAST:
+ self._events.register_last(event_name, handler)
+
+ def _register_config_store(self):
+ config_store_component = ConfigValueStore(
+ mapping=create_botocore_default_config_mapping(self)
+ )
+ self._components.register_component(
+ 'config_store', config_store_component
+ )
+
+ def _register_monitor(self):
+ self._internal_components.lazy_register_component(
+ 'monitor', self._create_csm_monitor
+ )
+
+ def _register_user_agent_creator(self):
+ uas = UserAgentString.from_environment()
+ self._components.register_component('user_agent_creator', uas)
+
+ def _create_csm_monitor(self):
+ if self.get_config_variable('csm_enabled'):
+ client_id = self.get_config_variable('csm_client_id')
+ host = self.get_config_variable('csm_host')
+ port = self.get_config_variable('csm_port')
+ handler = monitoring.Monitor(
+ adapter=monitoring.MonitorEventAdapter(),
+ publisher=monitoring.SocketPublisher(
+ socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM),
+ host=host,
+ port=port,
+ serializer=monitoring.CSMSerializer(
+ csm_client_id=client_id
+ ),
+ ),
+ )
+ return handler
+ return None
+
+ def _get_crt_version(self):
+ user_agent_creator = self.get_component('user_agent_creator')
+ return user_agent_creator._crt_version or 'Unknown'
+
+ @property
+ def available_profiles(self):
+ return list(self._build_profile_map().keys())
+
+ def _build_profile_map(self):
+ # This will build the profile map if it has not been created,
+ # otherwise it will return the cached value. The profile map
+ # is a list of profile names, to the config values for the profile.
+ if self._profile_map is None:
+ self._profile_map = self.full_config['profiles']
+ return self._profile_map
+
+ @property
+ def profile(self):
+ if self._profile is None:
+ profile = self.get_config_variable('profile')
+ self._profile = profile
+ return self._profile
+
+ def get_config_variable(self, logical_name, methods=None):
+ if methods is not None:
+ return self._get_config_variable_with_custom_methods(
+ logical_name, methods
+ )
+ return self.get_component('config_store').get_config_variable(
+ logical_name
+ )
+
+ def _get_config_variable_with_custom_methods(self, logical_name, methods):
+ # If a custom list of methods was supplied we need to perserve the
+ # behavior with the new system. To do so a new chain that is a copy of
+ # the old one will be constructed, but only with the supplied methods
+ # being added to the chain. This chain will be consulted for a value
+ # and then thrown out. This is not efficient, nor is the methods arg
+ # used in botocore, this is just for backwards compatibility.
+ chain_builder = SubsetChainConfigFactory(session=self, methods=methods)
+ mapping = create_botocore_default_config_mapping(self)
+ for name, config_options in self.session_var_map.items():
+ config_name, env_vars, default, typecast = config_options
+ build_chain_config_args = {
+ 'conversion_func': typecast,
+ 'default': default,
+ }
+ if 'instance' in methods:
+ build_chain_config_args['instance_name'] = name
+ if 'env' in methods:
+ build_chain_config_args['env_var_names'] = env_vars
+ if 'config' in methods:
+ build_chain_config_args['config_property_name'] = config_name
+ mapping[name] = chain_builder.create_config_chain(
+ **build_chain_config_args
+ )
+ config_store_component = ConfigValueStore(mapping=mapping)
+ value = config_store_component.get_config_variable(logical_name)
+ return value
+
+ def set_config_variable(self, logical_name, value):
+ """Set a configuration variable to a specific value.
+
+ By using this method, you can override the normal lookup
+ process used in ``get_config_variable`` by explicitly setting
+ a value. Subsequent calls to ``get_config_variable`` will
+ use the ``value``. This gives you per-session specific
+ configuration values.
+
+ ::
+ >>> # Assume logical name 'foo' maps to env var 'FOO'
+ >>> os.environ['FOO'] = 'myvalue'
+ >>> s.get_config_variable('foo')
+ 'myvalue'
+ >>> s.set_config_variable('foo', 'othervalue')
+ >>> s.get_config_variable('foo')
+ 'othervalue'
+
+ :type logical_name: str
+ :param logical_name: The logical name of the session variable
+ you want to set. These are the keys in ``SESSION_VARIABLES``.
+ :param value: The value to associate with the config variable.
+
+ """
+ logger.debug(
+ "Setting config variable for %s to %r",
+ logical_name,
+ value,
+ )
+ self._session_instance_vars[logical_name] = value
+
+ def instance_variables(self):
+ return copy.copy(self._session_instance_vars)
+
+ def get_scoped_config(self):
+ """
+ Returns the config values from the config file scoped to the current
+ profile.
+
+ The configuration data is loaded **only** from the config file.
+ It does not resolve variables based on different locations
+ (e.g. first from the session instance, then from environment
+ variables, then from the config file). If you want this lookup
+ behavior, use the ``get_config_variable`` method instead.
+
+ Note that this configuration is specific to a single profile (the
+ ``profile`` session variable).
+
+ If the ``profile`` session variable is set and the profile does
+ not exist in the config file, a ``ProfileNotFound`` exception
+ will be raised.
+
+ :raises: ConfigNotFound, ConfigParseError, ProfileNotFound
+ :rtype: dict
+
+ """
+ profile_name = self.get_config_variable('profile')
+ profile_map = self._build_profile_map()
+ # If a profile is not explicitly set return the default
+ # profile config or an empty config dict if we don't have
+ # a default profile.
+ if profile_name is None:
+ return profile_map.get('default', {})
+ elif profile_name not in profile_map:
+ # Otherwise if they specified a profile, it has to
+ # exist (even if it's the default profile) otherwise
+ # we complain.
+ raise ProfileNotFound(profile=profile_name)
+ else:
+ return profile_map[profile_name]
+
+ @property
+ def full_config(self):
+ """Return the parsed config file.
+
+ The ``get_config`` method returns the config associated with the
+ specified profile. This property returns the contents of the
+ **entire** config file.
+
+ :rtype: dict
+ """
+ if self._config is None:
+ try:
+ config_file = self.get_config_variable('config_file')
+ self._config = botocore.configloader.load_config(config_file)
+ except ConfigNotFound:
+ self._config = {'profiles': {}}
+ try:
+ # Now we need to inject the profiles from the
+ # credentials file. We don't actually need the values
+ # in the creds file, only the profile names so that we
+ # can validate the user is not referring to a nonexistent
+ # profile.
+ cred_file = self.get_config_variable('credentials_file')
+ cred_profiles = botocore.configloader.raw_config_parse(
+ cred_file
+ )
+ for profile in cred_profiles:
+ cred_vars = cred_profiles[profile]
+ if profile not in self._config['profiles']:
+ self._config['profiles'][profile] = cred_vars
+ else:
+ self._config['profiles'][profile].update(cred_vars)
+ except ConfigNotFound:
+ pass
+ return self._config
+
+ def get_default_client_config(self):
+ """Retrieves the default config for creating clients
+
+ :rtype: botocore.client.Config
+ :returns: The default client config object when creating clients. If
+ the value is ``None`` then there is no default config object
+ attached to the session.
+ """
+ return self._client_config
+
+ def set_default_client_config(self, client_config):
+ """Sets the default config for creating clients
+
+ :type client_config: botocore.client.Config
+ :param client_config: The default client config object when creating
+ clients. If the value is ``None`` then there is no default config
+ object attached to the session.
+ """
+ self._client_config = client_config
+
+ def set_credentials(
+ self, access_key, secret_key, token=None, account_id=None
+ ):
+ """
+ Manually create credentials for this session. If you would
+ prefer to use botocore without a config file, environment variables,
+ or IAM roles, you can pass explicit credentials into this
+ method to establish credentials for this session.
+
+ :type access_key: str
+ :param access_key: The access key part of the credentials.
+
+ :type secret_key: str
+ :param secret_key: The secret key part of the credentials.
+
+ :type token: str
+ :param token: An option session token used by STS session
+ credentials.
+
+ :type account_id: str
+ :param account_id: An optional account ID part of the credentials.
+ """
+ self._credentials = botocore.credentials.Credentials(
+ access_key, secret_key, token, account_id=account_id
+ )
+
+ def get_credentials(self):
+ """
+ Return the :class:`botocore.credential.Credential` object
+ associated with this session. If the credentials have not
+ yet been loaded, this will attempt to load them. If they
+ have already been loaded, this will return the cached
+ credentials.
+
+ """
+ if self._credentials is None:
+ self._credentials = self._components.get_component(
+ 'credential_provider'
+ ).load_credentials()
+ return self._credentials
+
+ def get_auth_token(self):
+ """
+ Return the :class:`botocore.tokens.AuthToken` object associated with
+ this session. If the authorization token has not yet been loaded, this
+ will attempt to load it. If it has already been loaded, this will
+ return the cached authorization token.
+
+ """
+ if self._auth_token is None:
+ provider = self._components.get_component('token_provider')
+ self._auth_token = provider.load_token()
+ return self._auth_token
+
+ def user_agent(self):
+ """
+ Return a string suitable for use as a User-Agent header.
+ The string will be of the form:
+
+ <agent_name>/<agent_version> Python/<py_ver> <plat_name>/<plat_ver> <exec_env>
+
+ Where:
+
+ - agent_name is the value of the `user_agent_name` attribute
+ of the session object (`Botocore` by default).
+ - agent_version is the value of the `user_agent_version`
+ attribute of the session object (the botocore version by default).
+ by default.
+ - py_ver is the version of the Python interpreter beng used.
+ - plat_name is the name of the platform (e.g. Darwin)
+ - plat_ver is the version of the platform
+ - exec_env is exec-env/$AWS_EXECUTION_ENV
+
+ If ``user_agent_extra`` is not empty, then this value will be
+ appended to the end of the user agent string.
+
+ """
+ base = (
+ f'{self.user_agent_name}/{self.user_agent_version} '
+ f'Python/{platform.python_version()} '
+ f'{platform.system()}/{platform.release()}'
+ )
+ if HAS_CRT:
+ base += f' awscrt/{self._get_crt_version()}'
+ if os.environ.get('AWS_EXECUTION_ENV') is not None:
+ base += ' exec-env/{}'.format(os.environ.get('AWS_EXECUTION_ENV'))
+ if self.user_agent_extra:
+ base += f' {self.user_agent_extra}'
+
+ return base
+
+ def get_data(self, data_path):
+ """
+ Retrieve the data associated with `data_path`.
+
+ :type data_path: str
+ :param data_path: The path to the data you wish to retrieve.
+ """
+ return self.get_component('data_loader').load_data(data_path)
+
+ def get_service_model(self, service_name, api_version=None):
+ """Get the service model object.
+
+ :type service_name: string
+ :param service_name: The service name
+
+ :type api_version: string
+ :param api_version: The API version of the service. If none is
+ provided, then the latest API version will be used.
+
+ :rtype: L{botocore.model.ServiceModel}
+ :return: The botocore service model for the service.
+
+ """
+ service_description = self.get_service_data(service_name, api_version)
+ return ServiceModel(service_description, service_name=service_name)
+
+ def get_waiter_model(self, service_name, api_version=None):
+ loader = self.get_component('data_loader')
+ waiter_config = loader.load_service_model(
+ service_name, 'waiters-2', api_version
+ )
+ return waiter.WaiterModel(waiter_config)
+
+ def get_paginator_model(self, service_name, api_version=None):
+ loader = self.get_component('data_loader')
+ paginator_config = loader.load_service_model(
+ service_name, 'paginators-1', api_version
+ )
+ return paginate.PaginatorModel(paginator_config)
+
+ def get_service_data(self, service_name, api_version=None):
+ """
+ Retrieve the fully merged data associated with a service.
+ """
+ data_path = service_name
+ service_data = self.get_component('data_loader').load_service_model(
+ data_path, type_name='service-2', api_version=api_version
+ )
+ service_id = EVENT_ALIASES.get(service_name, service_name)
+ self._events.emit(
+ f'service-data-loaded.{service_id}',
+ service_data=service_data,
+ service_name=service_name,
+ session=self,
+ )
+ return service_data
+
+ def get_available_services(self):
+ """
+ Return a list of names of available services.
+ """
+ return self.get_component('data_loader').list_available_services(
+ type_name='service-2'
+ )
+
+ def set_debug_logger(self, logger_name='botocore'):
+ """
+ Convenience function to quickly configure full debug output
+ to go to the console.
+ """
+ self.set_stream_logger(logger_name, logging.DEBUG)
+
+ def set_stream_logger(
+ self, logger_name, log_level, stream=None, format_string=None
+ ):
+ """
+ Convenience method to configure a stream logger.
+
+ :type logger_name: str
+ :param logger_name: The name of the logger to configure
+
+ :type log_level: str
+ :param log_level: The log level to set for the logger. This
+ is any param supported by the ``.setLevel()`` method of
+ a ``Log`` object.
+
+ :type stream: file
+ :param stream: A file like object to log to. If none is provided
+ then sys.stderr will be used.
+
+ :type format_string: str
+ :param format_string: The format string to use for the log
+ formatter. If none is provided this will default to
+ ``self.LOG_FORMAT``.
+
+ """
+ log = logging.getLogger(logger_name)
+ log.setLevel(logging.DEBUG)
+
+ ch = logging.StreamHandler(stream)
+ ch.setLevel(log_level)
+
+ # create formatter
+ if format_string is None:
+ format_string = self.LOG_FORMAT
+ formatter = logging.Formatter(format_string)
+
+ # add formatter to ch
+ ch.setFormatter(formatter)
+
+ # add ch to logger
+ log.addHandler(ch)
+
+ def set_file_logger(self, log_level, path, logger_name='botocore'):
+ """
+ Convenience function to quickly configure any level of logging
+ to a file.
+
+ :type log_level: int
+ :param log_level: A log level as specified in the `logging` module
+
+ :type path: string
+ :param path: Path to the log file. The file will be created
+ if it doesn't already exist.
+ """
+ log = logging.getLogger(logger_name)
+ log.setLevel(logging.DEBUG)
+
+ # create console handler and set level to debug
+ ch = logging.FileHandler(path)
+ ch.setLevel(log_level)
+
+ # create formatter
+ formatter = logging.Formatter(self.LOG_FORMAT)
+
+ # add formatter to ch
+ ch.setFormatter(formatter)
+
+ # add ch to logger
+ log.addHandler(ch)
+
+ def register(
+ self, event_name, handler, unique_id=None, unique_id_uses_count=False
+ ):
+ """Register a handler with an event.
+
+ :type event_name: str
+ :param event_name: The name of the event.
+
+ :type handler: callable
+ :param handler: The callback to invoke when the event
+ is emitted. This object must be callable, and must
+ accept ``**kwargs``. If either of these preconditions are
+ not met, a ``ValueError`` will be raised.
+
+ :type unique_id: str
+ :param unique_id: An optional identifier to associate with the
+ registration. A unique_id can only be used once for
+ the entire session registration (unless it is unregistered).
+ This can be used to prevent an event handler from being
+ registered twice.
+
+ :param unique_id_uses_count: boolean
+ :param unique_id_uses_count: Specifies if the event should maintain
+ a count when a ``unique_id`` is registered and unregisted. The
+ event can only be completely unregistered once every register call
+ using the unique id has been matched by an ``unregister`` call.
+ If ``unique_id`` is specified, subsequent ``register``
+ calls must use the same value for ``unique_id_uses_count``
+ as the ``register`` call that first registered the event.
+
+ :raises ValueError: If the call to ``register`` uses ``unique_id``
+ but the value for ``unique_id_uses_count`` differs from the
+ ``unique_id_uses_count`` value declared by the very first
+ ``register`` call for that ``unique_id``.
+ """
+ self._events.register(
+ event_name,
+ handler,
+ unique_id,
+ unique_id_uses_count=unique_id_uses_count,
+ )
+
+ def unregister(
+ self,
+ event_name,
+ handler=None,
+ unique_id=None,
+ unique_id_uses_count=False,
+ ):
+ """Unregister a handler with an event.
+
+ :type event_name: str
+ :param event_name: The name of the event.
+
+ :type handler: callable
+ :param handler: The callback to unregister.
+
+ :type unique_id: str
+ :param unique_id: A unique identifier identifying the callback
+ to unregister. You can provide either the handler or the
+ unique_id, you do not have to provide both.
+
+ :param unique_id_uses_count: boolean
+ :param unique_id_uses_count: Specifies if the event should maintain
+ a count when a ``unique_id`` is registered and unregisted. The
+ event can only be completely unregistered once every ``register``
+ call using the ``unique_id`` has been matched by an ``unregister``
+ call. If the ``unique_id`` is specified, subsequent
+ ``unregister`` calls must use the same value for
+ ``unique_id_uses_count`` as the ``register`` call that first
+ registered the event.
+
+ :raises ValueError: If the call to ``unregister`` uses ``unique_id``
+ but the value for ``unique_id_uses_count`` differs from the
+ ``unique_id_uses_count`` value declared by the very first
+ ``register`` call for that ``unique_id``.
+ """
+ self._events.unregister(
+ event_name,
+ handler=handler,
+ unique_id=unique_id,
+ unique_id_uses_count=unique_id_uses_count,
+ )
+
+ def emit(self, event_name, **kwargs):
+ return self._events.emit(event_name, **kwargs)
+
+ def emit_first_non_none_response(self, event_name, **kwargs):
+ responses = self._events.emit(event_name, **kwargs)
+ return first_non_none_response(responses)
+
+ def get_component(self, name):
+ try:
+ return self._components.get_component(name)
+ except ValueError:
+ if name in ['endpoint_resolver', 'exceptions_factory']:
+ warnings.warn(
+ f'Fetching the {name} component with the get_component() '
+ 'method is deprecated as the component has always been '
+ 'considered an internal interface of botocore',
+ DeprecationWarning,
+ )
+ return self._internal_components.get_component(name)
+ raise
+
+ def _get_internal_component(self, name):
+ # While this method may be called by botocore classes outside of the
+ # Session, this method should **never** be used by a class that lives
+ # outside of botocore.
+ return self._internal_components.get_component(name)
+
+ def _register_internal_component(self, name, component):
+ # While this method may be called by botocore classes outside of the
+ # Session, this method should **never** be used by a class that lives
+ # outside of botocore.
+ return self._internal_components.register_component(name, component)
+
+ def register_component(self, name, component):
+ self._components.register_component(name, component)
+
+ def lazy_register_component(self, name, component):
+ self._components.lazy_register_component(name, component)
+
+ @with_current_context()
+ def create_client(
+ self,
+ service_name,
+ region_name=None,
+ api_version=None,
+ use_ssl=True,
+ verify=None,
+ endpoint_url=None,
+ aws_access_key_id=None,
+ aws_secret_access_key=None,
+ aws_session_token=None,
+ config=None,
+ aws_account_id=None,
+ ):
+ """Create a botocore client.
+
+ :type service_name: string
+ :param service_name: The name of the service for which a client will
+ be created. You can use the ``Session.get_available_services()``
+ method to get a list of all available service names.
+
+ :type region_name: string
+ :param region_name: The name of the region associated with the client.
+ A client is associated with a single region.
+
+ :type api_version: string
+ :param api_version: The API version to use. By default, botocore will
+ use the latest API version when creating a client. You only need
+ to specify this parameter if you want to use a previous API version
+ of the client.
+
+ :type use_ssl: boolean
+ :param use_ssl: Whether or not to use SSL. By default, SSL is used.
+ Note that not all services support non-ssl connections.
+
+ :type verify: boolean/string
+ :param verify: Whether or not to verify SSL certificates.
+ By default SSL certificates are verified. You can provide the
+ following values:
+
+ * False - do not validate SSL certificates. SSL will still be
+ used (unless use_ssl is False), but SSL certificates
+ will not be verified.
+ * path/to/cert/bundle.pem - A filename of the CA cert bundle to
+ uses. You can specify this argument if you want to use a
+ different CA cert bundle than the one used by botocore.
+
+ :type endpoint_url: string
+ :param endpoint_url: The complete URL to use for the constructed
+ client. Normally, botocore will automatically construct the
+ appropriate URL to use when communicating with a service. You can
+ specify a complete URL (including the "http/https" scheme) to
+ override this behavior. If this value is provided, then
+ ``use_ssl`` is ignored.
+
+ :type aws_access_key_id: string
+ :param aws_access_key_id: The access key to use when creating
+ the client. This is entirely optional, and if not provided,
+ the credentials configured for the session will automatically
+ be used. You only need to provide this argument if you want
+ to override the credentials used for this specific client.
+
+ :type aws_secret_access_key: string
+ :param aws_secret_access_key: The secret key to use when creating
+ the client. Same semantics as aws_access_key_id above.
+
+ :type aws_session_token: string
+ :param aws_session_token: The session token to use when creating
+ the client. Same semantics as aws_access_key_id above.
+
+ :type config: botocore.client.Config
+ :param config: Advanced client configuration options. If a value
+ is specified in the client config, its value will take precedence
+ over environment variables and configuration values, but not over
+ a value passed explicitly to the method. If a default config
+ object is set on the session, the config object used when creating
+ the client will be the result of calling ``merge()`` on the
+ default config with the config provided to this call.
+
+ :type aws_account_id: string
+ :param aws_account_id: The account id to use when creating
+ the client. Same semantics as aws_access_key_id above.
+
+ :rtype: botocore.client.BaseClient
+ :return: A botocore client instance
+
+ """
+ default_client_config = self.get_default_client_config()
+ # If a config is provided and a default config is set, then
+ # use the config resulting from merging the two.
+ if config is not None and default_client_config is not None:
+ config = default_client_config.merge(config)
+ # If a config was not provided then use the default
+ # client config from the session
+ elif default_client_config is not None:
+ config = default_client_config
+
+ region_name = self._resolve_region_name(region_name, config)
+
+ # Figure out the verify value base on the various
+ # configuration options.
+ if verify is None:
+ verify = self.get_config_variable('ca_bundle')
+
+ if api_version is None:
+ api_version = self.get_config_variable('api_versions').get(
+ service_name, None
+ )
+
+ loader = self.get_component('data_loader')
+ event_emitter = self.get_component('event_emitter')
+ response_parser_factory = self.get_component('response_parser_factory')
+ if config is not None and config.signature_version is UNSIGNED:
+ credentials = None
+ elif (
+ aws_access_key_id is not None and aws_secret_access_key is not None
+ ):
+ credentials = botocore.credentials.Credentials(
+ access_key=aws_access_key_id,
+ secret_key=aws_secret_access_key,
+ token=aws_session_token,
+ account_id=aws_account_id,
+ )
+ elif self._missing_cred_vars(aws_access_key_id, aws_secret_access_key):
+ raise PartialCredentialsError(
+ provider='explicit',
+ cred_var=self._missing_cred_vars(
+ aws_access_key_id, aws_secret_access_key
+ ),
+ )
+ else:
+ if ignored_credentials := self._get_ignored_credentials(
+ aws_session_token, aws_account_id
+ ):
+ logger.debug(
+ f"Ignoring the following credential-related values which were set without "
+ f"an access key id and secret key on the session or client: {ignored_credentials}"
+ )
+ credentials = self.get_credentials()
+ auth_token = self.get_auth_token()
+ endpoint_resolver = self._get_internal_component('endpoint_resolver')
+ exceptions_factory = self._get_internal_component('exceptions_factory')
+ config_store = copy.copy(self.get_component('config_store'))
+ user_agent_creator = self.get_component('user_agent_creator')
+ # Session configuration values for the user agent string are applied
+ # just before each client creation because they may have been modified
+ # at any time between session creation and client creation.
+ user_agent_creator.set_session_config(
+ session_user_agent_name=self.user_agent_name,
+ session_user_agent_version=self.user_agent_version,
+ session_user_agent_extra=self.user_agent_extra,
+ )
+ defaults_mode = self._resolve_defaults_mode(config, config_store)
+ if defaults_mode != 'legacy':
+ smart_defaults_factory = self._get_internal_component(
+ 'smart_defaults_factory'
+ )
+ smart_defaults_factory.merge_smart_defaults(
+ config_store, defaults_mode, region_name
+ )
+
+ self._add_configured_endpoint_provider(
+ client_name=service_name,
+ config_store=config_store,
+ )
+
+ user_agent_creator.set_client_features(get_context().features)
+
+ client_creator = botocore.client.ClientCreator(
+ loader,
+ endpoint_resolver,
+ self.user_agent(),
+ event_emitter,
+ retryhandler,
+ translate,
+ response_parser_factory,
+ exceptions_factory,
+ config_store,
+ user_agent_creator=user_agent_creator,
+ )
+ client = client_creator.create_client(
+ service_name=service_name,
+ region_name=region_name,
+ is_secure=use_ssl,
+ endpoint_url=endpoint_url,
+ verify=verify,
+ credentials=credentials,
+ scoped_config=self.get_scoped_config(),
+ client_config=config,
+ api_version=api_version,
+ auth_token=auth_token,
+ )
+ monitor = self._get_internal_component('monitor')
+ if monitor is not None:
+ monitor.register(client.meta.events)
+ return client
+
+ def _resolve_region_name(self, region_name, config):
+ # Figure out the user-provided region based on the various
+ # configuration options.
+ if region_name is None:
+ if config and config.region_name is not None:
+ region_name = config.region_name
+ else:
+ region_name = self.get_config_variable('region')
+
+ validate_region_name(region_name)
+ # For any client that we create in retrieving credentials
+ # we want to create it using the same region as specified in
+ # creating this client. It is important to note though that the
+ # credentials client is only created once per session. So if a new
+ # client is created with a different region, its credential resolver
+ # will use the region of the first client. However, that is not an
+ # issue as of now because the credential resolver uses only STS and
+ # the credentials returned at regional endpoints are valid across
+ # all regions in the partition.
+ self._last_client_region_used = region_name
+ return region_name
+
+ def _resolve_defaults_mode(self, client_config, config_store):
+ mode = config_store.get_config_variable('defaults_mode')
+
+ if client_config and client_config.defaults_mode:
+ mode = client_config.defaults_mode
+
+ default_config_resolver = self._get_internal_component(
+ 'default_config_resolver'
+ )
+ default_modes = default_config_resolver.get_default_modes()
+ lmode = mode.lower()
+ if lmode not in default_modes:
+ raise InvalidDefaultsMode(
+ mode=mode, valid_modes=', '.join(default_modes)
+ )
+
+ return lmode
+
+ def _add_configured_endpoint_provider(self, client_name, config_store):
+ chain = ConfiguredEndpointProvider(
+ full_config=self.full_config,
+ scoped_config=self.get_scoped_config(),
+ client_name=client_name,
+ )
+ config_store.set_config_provider(
+ logical_name='endpoint_url',
+ provider=chain,
+ )
+
+ def _missing_cred_vars(self, access_key, secret_key):
+ if access_key is not None and secret_key is None:
+ return 'aws_secret_access_key'
+ if secret_key is not None and access_key is None:
+ return 'aws_access_key_id'
+ return None
+
+ def get_available_partitions(self):
+ """Lists the available partitions found on disk
+
+ :rtype: list
+ :return: Returns a list of partition names (e.g., ["aws", "aws-cn"])
+ """
+ resolver = self._get_internal_component('endpoint_resolver')
+ return resolver.get_available_partitions()
+
+ def get_partition_for_region(self, region_name):
+ """Lists the partition name of a particular region.
+
+ :type region_name: string
+ :param region_name: Name of the region to list partition for (e.g.,
+ us-east-1).
+
+ :rtype: string
+ :return: Returns the respective partition name (e.g., aws).
+ """
+ resolver = self._get_internal_component('endpoint_resolver')
+ return resolver.get_partition_for_region(region_name)
+
+ def get_available_regions(
+ self, service_name, partition_name='aws', allow_non_regional=False
+ ):
+ """Lists the region and endpoint names of a particular partition.
+
+ :type service_name: string
+ :param service_name: Name of a service to list endpoint for (e.g., s3).
+ This parameter accepts a service name (e.g., "elb") or endpoint
+ prefix (e.g., "elasticloadbalancing").
+
+ :type partition_name: string
+ :param partition_name: Name of the partition to limit endpoints to.
+ (e.g., aws for the public AWS endpoints, aws-cn for AWS China
+ endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.
+
+ :type allow_non_regional: bool
+ :param allow_non_regional: Set to True to include endpoints that are
+ not regional endpoints (e.g., s3-external-1,
+ fips-us-gov-west-1, etc).
+ :return: Returns a list of endpoint names (e.g., ["us-east-1"]).
+ """
+ resolver = self._get_internal_component('endpoint_resolver')
+ results = []
+ try:
+ service_data = self.get_service_data(service_name)
+ endpoint_prefix = service_data['metadata'].get(
+ 'endpointPrefix', service_name
+ )
+ results = resolver.get_available_endpoints(
+ endpoint_prefix, partition_name, allow_non_regional
+ )
+ except UnknownServiceError:
+ pass
+ return results
+
+ def _get_ignored_credentials(self, aws_session_token, aws_account_id):
+ credential_inputs = []
+ if aws_session_token:
+ credential_inputs.append('aws_session_token')
+ if aws_account_id:
+ credential_inputs.append('aws_account_id')
+ return ', '.join(credential_inputs) if credential_inputs else None
+
+
+class ComponentLocator:
+ """Service locator for session components."""
+
+ def __init__(self):
+ self._components = {}
+ self._deferred = {}
+
+ def get_component(self, name):
+ if name in self._deferred:
+ factory = self._deferred[name]
+ self._components[name] = factory()
+ # Only delete the component from the deferred dict after
+ # successfully creating the object from the factory as well as
+ # injecting the instantiated value into the _components dict.
+ try:
+ del self._deferred[name]
+ except KeyError:
+ # If we get here, it's likely that get_component was called
+ # concurrently from multiple threads, and another thread
+ # already deleted the entry. This means the factory was
+ # probably called twice, but cleaning up the deferred entry
+ # should not crash outright.
+ pass
+ try:
+ return self._components[name]
+ except KeyError:
+ raise ValueError(f"Unknown component: {name}")
+
+ def register_component(self, name, component):
+ self._components[name] = component
+ try:
+ del self._deferred[name]
+ except KeyError:
+ pass
+
+ def lazy_register_component(self, name, no_arg_factory):
+ self._deferred[name] = no_arg_factory
+ try:
+ del self._components[name]
+ except KeyError:
+ pass
+
+
+class SessionVarDict(MutableMapping):
+ def __init__(self, session, session_vars):
+ self._session = session
+ self._store = copy.copy(session_vars)
+
+ def __getitem__(self, key):
+ return self._store[key]
+
+ def __setitem__(self, key, value):
+ self._store[key] = value
+ self._update_config_store_from_session_vars(key, value)
+
+ def __delitem__(self, key):
+ del self._store[key]
+
+ def __iter__(self):
+ return iter(self._store)
+
+ def __len__(self):
+ return len(self._store)
+
+ def _update_config_store_from_session_vars(
+ self, logical_name, config_options
+ ):
+ # This is for backwards compatibility. The new preferred way to
+ # modify configuration logic is to use the component system to get
+ # the config_store component from the session, and then update
+ # a key with a custom config provider(s).
+ # This backwards compatibility method takes the old session_vars
+ # list of tuples and and transforms that into a set of updates to
+ # the config_store component.
+ config_chain_builder = ConfigChainFactory(session=self._session)
+ config_name, env_vars, default, typecast = config_options
+ config_store = self._session.get_component('config_store')
+ config_store.set_config_provider(
+ logical_name,
+ config_chain_builder.create_config_chain(
+ instance_name=logical_name,
+ env_var_names=env_vars,
+ config_property_names=config_name,
+ default=default,
+ conversion_func=typecast,
+ ),
+ )
+
+
+class SubsetChainConfigFactory:
+ """A class for creating backwards compatible configuration chains.
+
+ This class can be used instead of
+ :class:`botocore.configprovider.ConfigChainFactory` to make it honor the
+ methods argument to get_config_variable. This class can be used to filter
+ out providers that are not in the methods tuple when creating a new config
+ chain.
+ """
+
+ def __init__(self, session, methods, environ=None):
+ self._factory = ConfigChainFactory(session, environ)
+ self._supported_methods = methods
+
+ def create_config_chain(
+ self,
+ instance_name=None,
+ env_var_names=None,
+ config_property_name=None,
+ default=None,
+ conversion_func=None,
+ ):
+ """Build a config chain following the standard botocore pattern.
+
+ This config chain factory will omit any providers not in the methods
+ tuple provided at initialization. For example if given the tuple
+ ('instance', 'config',) it will not inject the environment provider
+ into the standard config chain. This lets the botocore session support
+ the custom ``methods`` argument for all the default botocore config
+ variables when calling ``get_config_variable``.
+ """
+ if 'instance' not in self._supported_methods:
+ instance_name = None
+ if 'env' not in self._supported_methods:
+ env_var_names = None
+ if 'config' not in self._supported_methods:
+ config_property_name = None
+ return self._factory.create_config_chain(
+ instance_name=instance_name,
+ env_var_names=env_var_names,
+ config_property_names=config_property_name,
+ default=default,
+ conversion_func=conversion_func,
+ )
+
+
+def get_session(env_vars=None):
+ """
+ Return a new session object.
+ """
+ return Session(env_vars)