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 26from botocore import __version__ 27from botocore import UNSIGNED 28import botocore.configloader 29import botocore.credentials 30import botocore.client 31from botocore.configprovider import ConfigValueStore 32from botocore.configprovider import ConfigChainFactory 33from botocore.configprovider import create_botocore_default_config_mapping 34from botocore.configprovider import BOTOCORE_DEFAUT_SESSION_VARIABLES 35from botocore.exceptions import ConfigNotFound, ProfileNotFound 36from botocore.exceptions import UnknownServiceError, PartialCredentialsError 37from botocore.errorfactory import ClientExceptionsFactory 38from botocore import handlers 39from botocore.hooks import HierarchicalEmitter, first_non_none_response 40from botocore.hooks import EventAliaser 41from botocore.loaders import create_loader 42from botocore.parsers import ResponseParserFactory 43from botocore.regions import EndpointResolver 44from botocore.model import ServiceModel 45from botocore import monitoring 46from botocore import paginate 47from botocore import waiter 48from botocore import retryhandler, translate 49from botocore import utils 50from botocore.utils import EVENT_ALIASES 51from botocore.compat import MutableMapping 52 53 54logger = logging.getLogger(__name__) 55 56 57class Session(object): 58 """ 59 The Session object collects together useful functionality 60 from `botocore` as well as important data such as configuration 61 information and credentials into a single, easy-to-use object. 62 63 :ivar available_profiles: A list of profiles defined in the config 64 file associated with this session. 65 :ivar profile: The current profile. 66 """ 67 68 SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES) 69 70 #: The default format string to use when configuring the botocore logger. 71 LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 72 73 def __init__(self, session_vars=None, event_hooks=None, 74 include_builtin_handlers=True, profile=None): 75 """ 76 Create a new Session object. 77 78 :type session_vars: dict 79 :param session_vars: A dictionary that is used to override some or all 80 of the environment variables associated with this session. The 81 key/value pairs defined in this dictionary will override the 82 corresponding variables defined in ``SESSION_VARIABLES``. 83 84 :type event_hooks: BaseEventHooks 85 :param event_hooks: The event hooks object to use. If one is not 86 provided, an event hooks object will be automatically created 87 for you. 88 89 :type include_builtin_handlers: bool 90 :param include_builtin_handlers: Indicates whether or not to 91 automatically register builtin handlers. 92 93 :type profile: str 94 :param profile: The name of the profile to use for this 95 session. Note that the profile can only be set when 96 the session is created. 97 98 """ 99 if event_hooks is None: 100 self._original_handler = HierarchicalEmitter() 101 else: 102 self._original_handler = event_hooks 103 self._events = EventAliaser(self._original_handler) 104 if include_builtin_handlers: 105 self._register_builtin_handlers(self._events) 106 self.user_agent_name = 'Botocore' 107 self.user_agent_version = __version__ 108 self.user_agent_extra = '' 109 # The _profile attribute is just used to cache the value 110 # of the current profile to avoid going through the normal 111 # config lookup process each access time. 112 self._profile = None 113 self._config = None 114 self._credentials = None 115 self._profile_map = None 116 # This is a dict that stores per session specific config variable 117 # overrides via set_config_variable(). 118 self._session_instance_vars = {} 119 if profile is not None: 120 self._session_instance_vars['profile'] = profile 121 self._client_config = None 122 self._last_client_region_used = None 123 self._components = ComponentLocator() 124 self._internal_components = ComponentLocator() 125 self._register_components() 126 self.session_var_map = SessionVarDict(self, self.SESSION_VARIABLES) 127 if session_vars is not None: 128 self.session_var_map.update(session_vars) 129 130 def _register_components(self): 131 self._register_credential_provider() 132 self._register_data_loader() 133 self._register_endpoint_resolver() 134 self._register_event_emitter() 135 self._register_response_parser_factory() 136 self._register_exceptions_factory() 137 self._register_config_store() 138 self._register_monitor() 139 140 def _register_event_emitter(self): 141 self._components.register_component('event_emitter', self._events) 142 143 def _register_credential_provider(self): 144 self._components.lazy_register_component( 145 'credential_provider', self._create_credential_resolver) 146 147 def _create_credential_resolver(self): 148 return botocore.credentials.create_credential_resolver( 149 self, region_name=self._last_client_region_used 150 ) 151 152 def _register_data_loader(self): 153 self._components.lazy_register_component( 154 'data_loader', 155 lambda: create_loader(self.get_config_variable('data_path'))) 156 157 def _register_endpoint_resolver(self): 158 def create_default_resolver(): 159 loader = self.get_component('data_loader') 160 endpoints = loader.load_data('endpoints') 161 return EndpointResolver(endpoints) 162 self._internal_components.lazy_register_component( 163 'endpoint_resolver', create_default_resolver) 164 165 def _register_response_parser_factory(self): 166 self._components.register_component('response_parser_factory', 167 ResponseParserFactory()) 168 169 def _register_exceptions_factory(self): 170 self._internal_components.register_component( 171 'exceptions_factory', ClientExceptionsFactory()) 172 173 def _register_builtin_handlers(self, events): 174 for spec in handlers.BUILTIN_HANDLERS: 175 if len(spec) == 2: 176 event_name, handler = spec 177 self.register(event_name, handler) 178 else: 179 event_name, handler, register_type = spec 180 if register_type is handlers.REGISTER_FIRST: 181 self._events.register_first(event_name, handler) 182 elif register_type is handlers.REGISTER_LAST: 183 self._events.register_last(event_name, handler) 184 185 def _register_config_store(self): 186 config_store_component = ConfigValueStore( 187 mapping=create_botocore_default_config_mapping(self) 188 ) 189 self._components.register_component('config_store', 190 config_store_component) 191 192 def _register_monitor(self): 193 self._internal_components.lazy_register_component( 194 'monitor', self._create_csm_monitor) 195 196 def _create_csm_monitor(self): 197 if self.get_config_variable('csm_enabled'): 198 client_id = self.get_config_variable('csm_client_id') 199 host = self.get_config_variable('csm_host') 200 port = self.get_config_variable('csm_port') 201 handler = monitoring.Monitor( 202 adapter=monitoring.MonitorEventAdapter(), 203 publisher=monitoring.SocketPublisher( 204 socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM), 205 host=host, 206 port=port, 207 serializer=monitoring.CSMSerializer( 208 csm_client_id=client_id) 209 ) 210 ) 211 return handler 212 return None 213 214 @property 215 def available_profiles(self): 216 return list(self._build_profile_map().keys()) 217 218 def _build_profile_map(self): 219 # This will build the profile map if it has not been created, 220 # otherwise it will return the cached value. The profile map 221 # is a list of profile names, to the config values for the profile. 222 if self._profile_map is None: 223 self._profile_map = self.full_config['profiles'] 224 return self._profile_map 225 226 @property 227 def profile(self): 228 if self._profile is None: 229 profile = self.get_config_variable('profile') 230 self._profile = profile 231 return self._profile 232 233 def get_config_variable(self, logical_name, methods=None): 234 if methods is not None: 235 return self._get_config_variable_with_custom_methods( 236 logical_name, methods) 237 return self.get_component('config_store').get_config_variable( 238 logical_name) 239 240 def _get_config_variable_with_custom_methods(self, logical_name, methods): 241 # If a custom list of methods was supplied we need to perserve the 242 # behavior with the new system. To do so a new chain that is a copy of 243 # the old one will be constructed, but only with the supplied methods 244 # being added to the chain. This chain will be consulted for a value 245 # and then thrown out. This is not efficient, nor is the methods arg 246 # used in botocore, this is just for backwards compatibility. 247 chain_builder = SubsetChainConfigFactory(session=self, methods=methods) 248 mapping = create_botocore_default_config_mapping(self) 249 for name, config_options in self.session_var_map.items(): 250 config_name, env_vars, default, typecast = config_options 251 build_chain_config_args = { 252 'conversion_func': typecast, 253 'default': default, 254 } 255 if 'instance' in methods: 256 build_chain_config_args['instance_name'] = name 257 if 'env' in methods: 258 build_chain_config_args['env_var_names'] = env_vars 259 if 'config' in methods: 260 build_chain_config_args['config_property_name'] = config_name 261 mapping[name] = chain_builder.create_config_chain( 262 **build_chain_config_args 263 ) 264 config_store_component = ConfigValueStore( 265 mapping=mapping 266 ) 267 value = config_store_component.get_config_variable(logical_name) 268 return value 269 270 def set_config_variable(self, logical_name, value): 271 """Set a configuration variable to a specific value. 272 273 By using this method, you can override the normal lookup 274 process used in ``get_config_variable`` by explicitly setting 275 a value. Subsequent calls to ``get_config_variable`` will 276 use the ``value``. This gives you per-session specific 277 configuration values. 278 279 :: 280 >>> # Assume logical name 'foo' maps to env var 'FOO' 281 >>> os.environ['FOO'] = 'myvalue' 282 >>> s.get_config_variable('foo') 283 'myvalue' 284 >>> s.set_config_variable('foo', 'othervalue') 285 >>> s.get_config_variable('foo') 286 'othervalue' 287 288 :type logical_name: str 289 :param logical_name: The logical name of the session variable 290 you want to set. These are the keys in ``SESSION_VARIABLES``. 291 :param value: The value to associate with the config variable. 292 293 """ 294 logger.debug( 295 "Setting config variable for %s to %r", 296 logical_name, 297 value, 298 ) 299 self._session_instance_vars[logical_name] = value 300 301 def instance_variables(self): 302 return copy.copy(self._session_instance_vars) 303 304 def get_scoped_config(self): 305 """ 306 Returns the config values from the config file scoped to the current 307 profile. 308 309 The configuration data is loaded **only** from the config file. 310 It does not resolve variables based on different locations 311 (e.g. first from the session instance, then from environment 312 variables, then from the config file). If you want this lookup 313 behavior, use the ``get_config_variable`` method instead. 314 315 Note that this configuration is specific to a single profile (the 316 ``profile`` session variable). 317 318 If the ``profile`` session variable is set and the profile does 319 not exist in the config file, a ``ProfileNotFound`` exception 320 will be raised. 321 322 :raises: ConfigNotFound, ConfigParseError, ProfileNotFound 323 :rtype: dict 324 325 """ 326 profile_name = self.get_config_variable('profile') 327 profile_map = self._build_profile_map() 328 # If a profile is not explicitly set return the default 329 # profile config or an empty config dict if we don't have 330 # a default profile. 331 if profile_name is None: 332 return profile_map.get('default', {}) 333 elif profile_name not in profile_map: 334 # Otherwise if they specified a profile, it has to 335 # exist (even if it's the default profile) otherwise 336 # we complain. 337 raise ProfileNotFound(profile=profile_name) 338 else: 339 return profile_map[profile_name] 340 341 @property 342 def full_config(self): 343 """Return the parsed config file. 344 345 The ``get_config`` method returns the config associated with the 346 specified profile. This property returns the contents of the 347 **entire** config file. 348 349 :rtype: dict 350 """ 351 if self._config is None: 352 try: 353 config_file = self.get_config_variable('config_file') 354 self._config = botocore.configloader.load_config(config_file) 355 except ConfigNotFound: 356 self._config = {'profiles': {}} 357 try: 358 # Now we need to inject the profiles from the 359 # credentials file. We don't actually need the values 360 # in the creds file, only the profile names so that we 361 # can validate the user is not referring to a nonexistent 362 # profile. 363 cred_file = self.get_config_variable('credentials_file') 364 cred_profiles = botocore.configloader.raw_config_parse( 365 cred_file) 366 for profile in cred_profiles: 367 cred_vars = cred_profiles[profile] 368 if profile not in self._config['profiles']: 369 self._config['profiles'][profile] = cred_vars 370 else: 371 self._config['profiles'][profile].update(cred_vars) 372 except ConfigNotFound: 373 pass 374 return self._config 375 376 def get_default_client_config(self): 377 """Retrieves the default config for creating clients 378 379 :rtype: botocore.client.Config 380 :returns: The default client config object when creating clients. If 381 the value is ``None`` then there is no default config object 382 attached to the session. 383 """ 384 return self._client_config 385 386 def set_default_client_config(self, client_config): 387 """Sets the default config for creating clients 388 389 :type client_config: botocore.client.Config 390 :param client_config: The default client config object when creating 391 clients. If the value is ``None`` then there is no default config 392 object attached to the session. 393 """ 394 self._client_config = client_config 395 396 def set_credentials(self, access_key, secret_key, token=None): 397 """ 398 Manually create credentials for this session. If you would 399 prefer to use botocore without a config file, environment variables, 400 or IAM roles, you can pass explicit credentials into this 401 method to establish credentials for this session. 402 403 :type access_key: str 404 :param access_key: The access key part of the credentials. 405 406 :type secret_key: str 407 :param secret_key: The secret key part of the credentials. 408 409 :type token: str 410 :param token: An option session token used by STS session 411 credentials. 412 """ 413 self._credentials = botocore.credentials.Credentials(access_key, 414 secret_key, 415 token) 416 417 def get_credentials(self): 418 """ 419 Return the :class:`botocore.credential.Credential` object 420 associated with this session. If the credentials have not 421 yet been loaded, this will attempt to load them. If they 422 have already been loaded, this will return the cached 423 credentials. 424 425 """ 426 if self._credentials is None: 427 self._credentials = self._components.get_component( 428 'credential_provider').load_credentials() 429 return self._credentials 430 431 def user_agent(self): 432 """ 433 Return a string suitable for use as a User-Agent header. 434 The string will be of the form: 435 436 <agent_name>/<agent_version> Python/<py_ver> <plat_name>/<plat_ver> <exec_env> 437 438 Where: 439 440 - agent_name is the value of the `user_agent_name` attribute 441 of the session object (`Botocore` by default). 442 - agent_version is the value of the `user_agent_version` 443 attribute of the session object (the botocore version by default). 444 by default. 445 - py_ver is the version of the Python interpreter beng used. 446 - plat_name is the name of the platform (e.g. Darwin) 447 - plat_ver is the version of the platform 448 - exec_env is exec-env/$AWS_EXECUTION_ENV 449 450 If ``user_agent_extra`` is not empty, then this value will be 451 appended to the end of the user agent string. 452 453 """ 454 base = '%s/%s Python/%s %s/%s' % (self.user_agent_name, 455 self.user_agent_version, 456 platform.python_version(), 457 platform.system(), 458 platform.release()) 459 if os.environ.get('AWS_EXECUTION_ENV') is not None: 460 base += ' exec-env/%s' % os.environ.get('AWS_EXECUTION_ENV') 461 if self.user_agent_extra: 462 base += ' %s' % self.user_agent_extra 463 464 return base 465 466 def get_data(self, data_path): 467 """ 468 Retrieve the data associated with `data_path`. 469 470 :type data_path: str 471 :param data_path: The path to the data you wish to retrieve. 472 """ 473 return self.get_component('data_loader').load_data(data_path) 474 475 def get_service_model(self, service_name, api_version=None): 476 """Get the service model object. 477 478 :type service_name: string 479 :param service_name: The service name 480 481 :type api_version: string 482 :param api_version: The API version of the service. If none is 483 provided, then the latest API version will be used. 484 485 :rtype: L{botocore.model.ServiceModel} 486 :return: The botocore service model for the service. 487 488 """ 489 service_description = self.get_service_data(service_name, api_version) 490 return ServiceModel(service_description, service_name=service_name) 491 492 def get_waiter_model(self, service_name, api_version=None): 493 loader = self.get_component('data_loader') 494 waiter_config = loader.load_service_model( 495 service_name, 'waiters-2', api_version) 496 return waiter.WaiterModel(waiter_config) 497 498 def get_paginator_model(self, service_name, api_version=None): 499 loader = self.get_component('data_loader') 500 paginator_config = loader.load_service_model( 501 service_name, 'paginators-1', api_version) 502 return paginate.PaginatorModel(paginator_config) 503 504 def get_service_data(self, service_name, api_version=None): 505 """ 506 Retrieve the fully merged data associated with a service. 507 """ 508 data_path = service_name 509 service_data = self.get_component('data_loader').load_service_model( 510 data_path, 511 type_name='service-2', 512 api_version=api_version 513 ) 514 service_id = EVENT_ALIASES.get(service_name, service_name) 515 self._events.emit('service-data-loaded.%s' % service_id, 516 service_data=service_data, 517 service_name=service_name, session=self) 518 return service_data 519 520 def get_available_services(self): 521 """ 522 Return a list of names of available services. 523 """ 524 return self.get_component('data_loader')\ 525 .list_available_services(type_name='service-2') 526 527 def set_debug_logger(self, logger_name='botocore'): 528 """ 529 Convenience function to quickly configure full debug output 530 to go to the console. 531 """ 532 self.set_stream_logger(logger_name, logging.DEBUG) 533 534 def set_stream_logger(self, logger_name, log_level, stream=None, 535 format_string=None): 536 """ 537 Convenience method to configure a stream logger. 538 539 :type logger_name: str 540 :param logger_name: The name of the logger to configure 541 542 :type log_level: str 543 :param log_level: The log level to set for the logger. This 544 is any param supported by the ``.setLevel()`` method of 545 a ``Log`` object. 546 547 :type stream: file 548 :param stream: A file like object to log to. If none is provided 549 then sys.stderr will be used. 550 551 :type format_string: str 552 :param format_string: The format string to use for the log 553 formatter. If none is provided this will default to 554 ``self.LOG_FORMAT``. 555 556 """ 557 log = logging.getLogger(logger_name) 558 log.setLevel(logging.DEBUG) 559 560 ch = logging.StreamHandler(stream) 561 ch.setLevel(log_level) 562 563 # create formatter 564 if format_string is None: 565 format_string = self.LOG_FORMAT 566 formatter = logging.Formatter(format_string) 567 568 # add formatter to ch 569 ch.setFormatter(formatter) 570 571 # add ch to logger 572 log.addHandler(ch) 573 574 def set_file_logger(self, log_level, path, logger_name='botocore'): 575 """ 576 Convenience function to quickly configure any level of logging 577 to a file. 578 579 :type log_level: int 580 :param log_level: A log level as specified in the `logging` module 581 582 :type path: string 583 :param path: Path to the log file. The file will be created 584 if it doesn't already exist. 585 """ 586 log = logging.getLogger(logger_name) 587 log.setLevel(logging.DEBUG) 588 589 # create console handler and set level to debug 590 ch = logging.FileHandler(path) 591 ch.setLevel(log_level) 592 593 # create formatter 594 formatter = logging.Formatter(self.LOG_FORMAT) 595 596 # add formatter to ch 597 ch.setFormatter(formatter) 598 599 # add ch to logger 600 log.addHandler(ch) 601 602 def register(self, event_name, handler, unique_id=None, 603 unique_id_uses_count=False): 604 """Register a handler with an event. 605 606 :type event_name: str 607 :param event_name: The name of the event. 608 609 :type handler: callable 610 :param handler: The callback to invoke when the event 611 is emitted. This object must be callable, and must 612 accept ``**kwargs``. If either of these preconditions are 613 not met, a ``ValueError`` will be raised. 614 615 :type unique_id: str 616 :param unique_id: An optional identifier to associate with the 617 registration. A unique_id can only be used once for 618 the entire session registration (unless it is unregistered). 619 This can be used to prevent an event handler from being 620 registered twice. 621 622 :param unique_id_uses_count: boolean 623 :param unique_id_uses_count: Specifies if the event should maintain 624 a count when a ``unique_id`` is registered and unregisted. The 625 event can only be completely unregistered once every register call 626 using the unique id has been matched by an ``unregister`` call. 627 If ``unique_id`` is specified, subsequent ``register`` 628 calls must use the same value for ``unique_id_uses_count`` 629 as the ``register`` call that first registered the event. 630 631 :raises ValueError: If the call to ``register`` uses ``unique_id`` 632 but the value for ``unique_id_uses_count`` differs from the 633 ``unique_id_uses_count`` value declared by the very first 634 ``register`` call for that ``unique_id``. 635 """ 636 self._events.register(event_name, handler, unique_id, 637 unique_id_uses_count=unique_id_uses_count) 638 639 def unregister(self, event_name, handler=None, unique_id=None, 640 unique_id_uses_count=False): 641 """Unregister a handler with an event. 642 643 :type event_name: str 644 :param event_name: The name of the event. 645 646 :type handler: callable 647 :param handler: The callback to unregister. 648 649 :type unique_id: str 650 :param unique_id: A unique identifier identifying the callback 651 to unregister. You can provide either the handler or the 652 unique_id, you do not have to provide both. 653 654 :param unique_id_uses_count: boolean 655 :param unique_id_uses_count: Specifies if the event should maintain 656 a count when a ``unique_id`` is registered and unregisted. The 657 event can only be completely unregistered once every ``register`` 658 call using the ``unique_id`` has been matched by an ``unregister`` 659 call. If the ``unique_id`` is specified, subsequent 660 ``unregister`` calls must use the same value for 661 ``unique_id_uses_count`` as the ``register`` call that first 662 registered the event. 663 664 :raises ValueError: If the call to ``unregister`` uses ``unique_id`` 665 but the value for ``unique_id_uses_count`` differs from the 666 ``unique_id_uses_count`` value declared by the very first 667 ``register`` call for that ``unique_id``. 668 """ 669 self._events.unregister(event_name, handler=handler, 670 unique_id=unique_id, 671 unique_id_uses_count=unique_id_uses_count) 672 673 def emit(self, event_name, **kwargs): 674 return self._events.emit(event_name, **kwargs) 675 676 def emit_first_non_none_response(self, event_name, **kwargs): 677 responses = self._events.emit(event_name, **kwargs) 678 return first_non_none_response(responses) 679 680 def get_component(self, name): 681 try: 682 return self._components.get_component(name) 683 except ValueError: 684 if name in ['endpoint_resolver', 'exceptions_factory']: 685 warnings.warn( 686 'Fetching the %s component with the get_component() ' 687 'method is deprecated as the component has always been ' 688 'considered an internal interface of botocore' % name, 689 DeprecationWarning) 690 return self._internal_components.get_component(name) 691 raise 692 693 def _get_internal_component(self, name): 694 # While this method may be called by botocore classes outside of the 695 # Session, this method should **never** be used by a class that lives 696 # outside of botocore. 697 return self._internal_components.get_component(name) 698 699 def _register_internal_component(self, name, component): 700 # While this method may be called by botocore classes outside of the 701 # Session, this method should **never** be used by a class that lives 702 # outside of botocore. 703 return self._internal_components.register_component(name, component) 704 705 def register_component(self, name, component): 706 self._components.register_component(name, component) 707 708 def lazy_register_component(self, name, component): 709 self._components.lazy_register_component(name, component) 710 711 def create_client(self, service_name, region_name=None, api_version=None, 712 use_ssl=True, verify=None, endpoint_url=None, 713 aws_access_key_id=None, aws_secret_access_key=None, 714 aws_session_token=None, config=None): 715 """Create a botocore client. 716 717 :type service_name: string 718 :param service_name: The name of the service for which a client will 719 be created. You can use the ``Sesssion.get_available_services()`` 720 method to get a list of all available service names. 721 722 :type region_name: string 723 :param region_name: The name of the region associated with the client. 724 A client is associated with a single region. 725 726 :type api_version: string 727 :param api_version: The API version to use. By default, botocore will 728 use the latest API version when creating a client. You only need 729 to specify this parameter if you want to use a previous API version 730 of the client. 731 732 :type use_ssl: boolean 733 :param use_ssl: Whether or not to use SSL. By default, SSL is used. 734 Note that not all services support non-ssl connections. 735 736 :type verify: boolean/string 737 :param verify: Whether or not to verify SSL certificates. 738 By default SSL certificates are verified. You can provide the 739 following values: 740 741 * False - do not validate SSL certificates. SSL will still be 742 used (unless use_ssl is False), but SSL certificates 743 will not be verified. 744 * path/to/cert/bundle.pem - A filename of the CA cert bundle to 745 uses. You can specify this argument if you want to use a 746 different CA cert bundle than the one used by botocore. 747 748 :type endpoint_url: string 749 :param endpoint_url: The complete URL to use for the constructed 750 client. Normally, botocore will automatically construct the 751 appropriate URL to use when communicating with a service. You can 752 specify a complete URL (including the "http/https" scheme) to 753 override this behavior. If this value is provided, then 754 ``use_ssl`` is ignored. 755 756 :type aws_access_key_id: string 757 :param aws_access_key_id: The access key to use when creating 758 the client. This is entirely optional, and if not provided, 759 the credentials configured for the session will automatically 760 be used. You only need to provide this argument if you want 761 to override the credentials used for this specific client. 762 763 :type aws_secret_access_key: string 764 :param aws_secret_access_key: The secret key to use when creating 765 the client. Same semantics as aws_access_key_id above. 766 767 :type aws_session_token: string 768 :param aws_session_token: The session token to use when creating 769 the client. Same semantics as aws_access_key_id above. 770 771 :type config: botocore.client.Config 772 :param config: Advanced client configuration options. If a value 773 is specified in the client config, its value will take precedence 774 over environment variables and configuration values, but not over 775 a value passed explicitly to the method. If a default config 776 object is set on the session, the config object used when creating 777 the client will be the result of calling ``merge()`` on the 778 default config with the config provided to this call. 779 780 :rtype: botocore.client.BaseClient 781 :return: A botocore client instance 782 783 """ 784 default_client_config = self.get_default_client_config() 785 # If a config is provided and a default config is set, then 786 # use the config resulting from merging the two. 787 if config is not None and default_client_config is not None: 788 config = default_client_config.merge(config) 789 # If a config was not provided then use the default 790 # client config from the session 791 elif default_client_config is not None: 792 config = default_client_config 793 794 region_name = self._resolve_region_name(region_name, config) 795 796 # Figure out the verify value base on the various 797 # configuration options. 798 if verify is None: 799 verify = self.get_config_variable('ca_bundle') 800 801 if api_version is None: 802 api_version = self.get_config_variable('api_versions').get( 803 service_name, None) 804 805 loader = self.get_component('data_loader') 806 event_emitter = self.get_component('event_emitter') 807 response_parser_factory = self.get_component( 808 'response_parser_factory') 809 if config is not None and config.signature_version is UNSIGNED: 810 credentials = None 811 elif aws_access_key_id is not None and aws_secret_access_key is not None: 812 credentials = botocore.credentials.Credentials( 813 access_key=aws_access_key_id, 814 secret_key=aws_secret_access_key, 815 token=aws_session_token) 816 elif self._missing_cred_vars(aws_access_key_id, 817 aws_secret_access_key): 818 raise PartialCredentialsError( 819 provider='explicit', 820 cred_var=self._missing_cred_vars(aws_access_key_id, 821 aws_secret_access_key)) 822 else: 823 credentials = self.get_credentials() 824 endpoint_resolver = self._get_internal_component('endpoint_resolver') 825 exceptions_factory = self._get_internal_component('exceptions_factory') 826 config_store = self.get_component('config_store') 827 client_creator = botocore.client.ClientCreator( 828 loader, endpoint_resolver, self.user_agent(), event_emitter, 829 retryhandler, translate, response_parser_factory, 830 exceptions_factory, config_store) 831 client = client_creator.create_client( 832 service_name=service_name, region_name=region_name, 833 is_secure=use_ssl, endpoint_url=endpoint_url, verify=verify, 834 credentials=credentials, scoped_config=self.get_scoped_config(), 835 client_config=config, api_version=api_version) 836 monitor = self._get_internal_component('monitor') 837 if monitor is not None: 838 monitor.register(client.meta.events) 839 return client 840 841 def _resolve_region_name(self, region_name, config): 842 # Figure out the user-provided region based on the various 843 # configuration options. 844 if region_name is None: 845 if config and config.region_name is not None: 846 region_name = config.region_name 847 else: 848 region_name = self.get_config_variable('region') 849 # For any client that we create in retrieving credentials 850 # we want to create it using the same region as specified in 851 # creating this client. It is important to note though that the 852 # credentials client is only created once per session. So if a new 853 # client is created with a different region, its credential resolver 854 # will use the region of the first client. However, that is not an 855 # issue as of now because the credential resolver uses only STS and 856 # the credentials returned at regional endpoints are valid across 857 # all regions in the partition. 858 self._last_client_region_used = region_name 859 return region_name 860 861 def _missing_cred_vars(self, access_key, secret_key): 862 if access_key is not None and secret_key is None: 863 return 'aws_secret_access_key' 864 if secret_key is not None and access_key is None: 865 return 'aws_access_key_id' 866 return None 867 868 def get_available_partitions(self): 869 """Lists the available partitions found on disk 870 871 :rtype: list 872 :return: Returns a list of partition names (e.g., ["aws", "aws-cn"]) 873 """ 874 resolver = self._get_internal_component('endpoint_resolver') 875 return resolver.get_available_partitions() 876 877 def get_available_regions(self, service_name, partition_name='aws', 878 allow_non_regional=False): 879 """Lists the region and endpoint names of a particular partition. 880 881 :type service_name: string 882 :param service_name: Name of a service to list endpoint for (e.g., s3). 883 This parameter accepts a service name (e.g., "elb") or endpoint 884 prefix (e.g., "elasticloadbalancing"). 885 886 :type partition_name: string 887 :param partition_name: Name of the partition to limit endpoints to. 888 (e.g., aws for the public AWS endpoints, aws-cn for AWS China 889 endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc. 890 891 :type allow_non_regional: bool 892 :param allow_non_regional: Set to True to include endpoints that are 893 not regional endpoints (e.g., s3-external-1, 894 fips-us-gov-west-1, etc). 895 :return: Returns a list of endpoint names (e.g., ["us-east-1"]). 896 """ 897 resolver = self._get_internal_component('endpoint_resolver') 898 results = [] 899 try: 900 service_data = self.get_service_data(service_name) 901 endpoint_prefix = service_data['metadata'].get( 902 'endpointPrefix', service_name) 903 results = resolver.get_available_endpoints( 904 endpoint_prefix, partition_name, allow_non_regional) 905 except UnknownServiceError: 906 pass 907 return results 908 909 910class ComponentLocator(object): 911 """Service locator for session components.""" 912 def __init__(self): 913 self._components = {} 914 self._deferred = {} 915 916 def get_component(self, name): 917 if name in self._deferred: 918 factory = self._deferred[name] 919 self._components[name] = factory() 920 # Only delete the component from the deferred dict after 921 # successfully creating the object from the factory as well as 922 # injecting the instantiated value into the _components dict. 923 del self._deferred[name] 924 try: 925 return self._components[name] 926 except KeyError: 927 raise ValueError("Unknown component: %s" % name) 928 929 def register_component(self, name, component): 930 self._components[name] = component 931 try: 932 del self._deferred[name] 933 except KeyError: 934 pass 935 936 def lazy_register_component(self, name, no_arg_factory): 937 self._deferred[name] = no_arg_factory 938 try: 939 del self._components[name] 940 except KeyError: 941 pass 942 943 944class SessionVarDict(MutableMapping): 945 def __init__(self, session, session_vars): 946 self._session = session 947 self._store = copy.copy(session_vars) 948 949 def __getitem__(self, key): 950 return self._store[key] 951 952 def __setitem__(self, key, value): 953 self._store[key] = value 954 self._update_config_store_from_session_vars(key, value) 955 956 def __delitem__(self, key): 957 del self._store[key] 958 959 def __iter__(self): 960 return iter(self._store) 961 962 def __len__(self): 963 return len(self._store) 964 965 def _update_config_store_from_session_vars(self, logical_name, 966 config_options): 967 # This is for backwards compatibility. The new preferred way to 968 # modify configuration logic is to use the component system to get 969 # the config_store component from the session, and then update 970 # a key with a custom config provider(s). 971 # This backwards compatibility method takes the old session_vars 972 # list of tuples and and transforms that into a set of updates to 973 # the config_store component. 974 config_chain_builder = ConfigChainFactory(session=self._session) 975 config_name, env_vars, default, typecast = config_options 976 config_store = self._session.get_component('config_store') 977 config_store.set_config_provider( 978 logical_name, 979 config_chain_builder.create_config_chain( 980 instance_name=logical_name, 981 env_var_names=env_vars, 982 config_property_names=config_name, 983 default=default, 984 conversion_func=typecast, 985 ) 986 ) 987 988 989class SubsetChainConfigFactory(object): 990 """A class for creating backwards compatible configuration chains. 991 992 This class can be used instead of 993 :class:`botocore.configprovider.ConfigChainFactory` to make it honor the 994 methods argument to get_config_variable. This class can be used to filter 995 out providers that are not in the methods tuple when creating a new config 996 chain. 997 """ 998 def __init__(self, session, methods, environ=None): 999 self._factory = ConfigChainFactory(session, environ) 1000 self._supported_methods = methods 1001 1002 def create_config_chain(self, instance_name=None, env_var_names=None, 1003 config_property_name=None, default=None, 1004 conversion_func=None): 1005 """Build a config chain following the standard botocore pattern. 1006 1007 This config chain factory will omit any providers not in the methods 1008 tuple provided at initialization. For example if given the tuple 1009 ('instance', 'config',) it will not inject the environment provider 1010 into the standard config chain. This lets the botocore session support 1011 the custom ``methods`` argument for all the default botocore config 1012 variables when calling ``get_config_variable``. 1013 """ 1014 if 'instance' not in self._supported_methods: 1015 instance_name = None 1016 if 'env' not in self._supported_methods: 1017 env_var_names = None 1018 if 'config' not in self._supported_methods: 1019 config_property_name = None 1020 return self._factory.create_config_chain( 1021 instance_name=instance_name, 1022 env_var_names=env_var_names, 1023 config_property_names=config_property_name, 1024 default=default, 1025 conversion_func=conversion_func, 1026 ) 1027 1028 1029def get_session(env_vars=None): 1030 """ 1031 Return a new session object. 1032 """ 1033 return Session(env_vars) 1034