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 11
"""
15
This module contains the main interface to the botocore package, the
16
Session object.
17
"""
18

19 11
import copy
20 11
import logging
21 11
import os
22 11
import platform
23 11
import re
24 11
import socket
25 11
import warnings
26

27 11
from botocore import __version__
28 11
from botocore import UNSIGNED
29 11
import botocore.configloader
30 11
import botocore.credentials
31 11
import botocore.client
32 11
from botocore.configprovider import ConfigValueStore
33 11
from botocore.configprovider import ConfigChainFactory
34 11
from botocore.configprovider import create_botocore_default_config_mapping
35 11
from botocore.configprovider import BOTOCORE_DEFAUT_SESSION_VARIABLES
36 11
from botocore.exceptions import (
37
    ConfigNotFound, ProfileNotFound, UnknownServiceError,
38
    PartialCredentialsError,
39
)
40 11
from botocore.errorfactory import ClientExceptionsFactory
41 11
from botocore import handlers
42 11
from botocore.hooks import HierarchicalEmitter, first_non_none_response
43 11
from botocore.hooks import EventAliaser
44 11
from botocore.loaders import create_loader
45 11
from botocore.parsers import ResponseParserFactory
46 11
from botocore.regions import EndpointResolver
47 11
from botocore.model import ServiceModel
48 11
from botocore import monitoring
49 11
from botocore import paginate
50 11
from botocore import waiter
51 11
from botocore import retryhandler, translate
52 11
from botocore import utils
53 11
from botocore.utils import EVENT_ALIASES, validate_region_name
54 11
from botocore.compat import MutableMapping
55

56

57 11
logger = logging.getLogger(__name__)
58

59

60 11
class Session(object):
61
    """
62
    The Session object collects together useful functionality
63
    from `botocore` as well as important data such as configuration
64
    information and credentials into a single, easy-to-use object.
65

66
    :ivar available_profiles: A list of profiles defined in the config
67
        file associated with this session.
68
    :ivar profile: The current profile.
69
    """
70

71 11
    SESSION_VARIABLES = copy.copy(BOTOCORE_DEFAUT_SESSION_VARIABLES)
72

73
    #: The default format string to use when configuring the botocore logger.
74 11
    LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
75

76 11
    def __init__(self, session_vars=None, event_hooks=None,
77
                 include_builtin_handlers=True, profile=None):
78
        """
79
        Create a new Session object.
80

81
        :type session_vars: dict
82
        :param session_vars: A dictionary that is used to override some or all
83
            of the environment variables associated with this session.  The
84
            key/value pairs defined in this dictionary will override the
85
            corresponding variables defined in ``SESSION_VARIABLES``.
86

87
        :type event_hooks: BaseEventHooks
88
        :param event_hooks: The event hooks object to use. If one is not
89
            provided, an event hooks object will be automatically created
90
            for you.
91

92
        :type include_builtin_handlers: bool
93
        :param include_builtin_handlers: Indicates whether or not to
94
            automatically register builtin handlers.
95

96
        :type profile: str
97
        :param profile: The name of the profile to use for this
98
            session.  Note that the profile can only be set when
99
            the session is created.
100

101
        """
102 11
        if event_hooks is None:
103 11
            self._original_handler = HierarchicalEmitter()
104
        else:
105 11
            self._original_handler = event_hooks
106 11
        self._events = EventAliaser(self._original_handler)
107 11
        if include_builtin_handlers:
108 11
            self._register_builtin_handlers(self._events)
109 11
        self.user_agent_name = 'Botocore'
110 11
        self.user_agent_version = __version__
111 11
        self.user_agent_extra = ''
112
        # The _profile attribute is just used to cache the value
113
        # of the current profile to avoid going through the normal
114
        # config lookup process each access time.
115 11
        self._profile = None
116 11
        self._config = None
117 11
        self._credentials = None
118 11
        self._profile_map = None
119
        # This is a dict that stores per session specific config variable
120
        # overrides via set_config_variable().
121 11
        self._session_instance_vars = {}
122 11
        if profile is not None:
123 11
            self._session_instance_vars['profile'] = profile
124 11
        self._client_config = None
125 11
        self._last_client_region_used = None
126 11
        self._components = ComponentLocator()
127 11
        self._internal_components = ComponentLocator()
128 11
        self._register_components()
129 11
        self.session_var_map = SessionVarDict(self, self.SESSION_VARIABLES)
130 11
        if session_vars is not None:
131 11
            self.session_var_map.update(session_vars)
132

133 11
    def _register_components(self):
134 11
        self._register_credential_provider()
135 11
        self._register_data_loader()
136 11
        self._register_endpoint_resolver()
137 11
        self._register_event_emitter()
138 11
        self._register_response_parser_factory()
139 11
        self._register_exceptions_factory()
140 11
        self._register_config_store()
141 11
        self._register_monitor()
142

143 11
    def _register_event_emitter(self):
144 11
        self._components.register_component('event_emitter', self._events)
145

146 11
    def _register_credential_provider(self):
147 11
        self._components.lazy_register_component(
148
            'credential_provider', self._create_credential_resolver)
149

150 11
    def _create_credential_resolver(self):
151 11
        return botocore.credentials.create_credential_resolver(
152
            self, region_name=self._last_client_region_used
153
        )
154

155 11
    def _register_data_loader(self):
156 11
        self._components.lazy_register_component(
157
            'data_loader',
158
            lambda:  create_loader(self.get_config_variable('data_path')))
159

160 11
    def _register_endpoint_resolver(self):
161 11
        def create_default_resolver():
162 11
            loader = self.get_component('data_loader')
163 11
            endpoints = loader.load_data('endpoints')
164 11
            return EndpointResolver(endpoints)
165 11
        self._internal_components.lazy_register_component(
166
            'endpoint_resolver', create_default_resolver)
167

168 11
    def _register_response_parser_factory(self):
169 11
        self._components.register_component('response_parser_factory',
170
                                            ResponseParserFactory())
171

172 11
    def _register_exceptions_factory(self):
173 11
        self._internal_components.register_component(
174
            'exceptions_factory', ClientExceptionsFactory())
175

176 11
    def _register_builtin_handlers(self, events):
177 11
        for spec in handlers.BUILTIN_HANDLERS:
178 11
            if len(spec) == 2:
179 11
                event_name, handler = spec
180 11
                self.register(event_name, handler)
181
            else:
182 11
                event_name, handler, register_type = spec
183 11
                if register_type is handlers.REGISTER_FIRST:
184 11
                    self._events.register_first(event_name, handler)
185 11
                elif register_type is handlers.REGISTER_LAST:
186 11
                    self._events.register_last(event_name, handler)
187

188 11
    def _register_config_store(self):
189 11
        config_store_component = ConfigValueStore(
190
            mapping=create_botocore_default_config_mapping(self)
191
        )
192 11
        self._components.register_component('config_store',
193
                                            config_store_component)
194

195 11
    def _register_monitor(self):
196 11
        self._internal_components.lazy_register_component(
197
            'monitor', self._create_csm_monitor)
198

199 11
    def _create_csm_monitor(self):
200 11
        if self.get_config_variable('csm_enabled'):
201 11
            client_id = self.get_config_variable('csm_client_id')
202 11
            host = self.get_config_variable('csm_host')
203 11
            port = self.get_config_variable('csm_port')
204 11
            handler = monitoring.Monitor(
205
                adapter=monitoring.MonitorEventAdapter(),
206
                publisher=monitoring.SocketPublisher(
207
                    socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM),
208
                    host=host,
209
                    port=port,
210
                    serializer=monitoring.CSMSerializer(
211
                        csm_client_id=client_id)
212
                )
213
            )
214 11
            return handler
215 11
        return None
216

217 11
    @property
218 2
    def available_profiles(self):
219 11
        return list(self._build_profile_map().keys())
220

221 11
    def _build_profile_map(self):
222
        # This will build the profile map if it has not been created,
223
        # otherwise it will return the cached value.  The profile map
224
        # is a list of profile names, to the config values for the profile.
225 11
        if self._profile_map is None:
226 11
            self._profile_map = self.full_config['profiles']
227 11
        return self._profile_map
228

229 11
    @property
230 2
    def profile(self):
231 11
        if self._profile is None:
232 11
            profile = self.get_config_variable('profile')
233 11
            self._profile = profile
234 11
        return self._profile
235

236 11
    def get_config_variable(self, logical_name, methods=None):
237 11
        if methods is not None:
238 11
            return self._get_config_variable_with_custom_methods(
239
                logical_name, methods)
240 11
        return self.get_component('config_store').get_config_variable(
241
            logical_name)
242

243 11
    def _get_config_variable_with_custom_methods(self, logical_name, methods):
244
        # If a custom list of methods was supplied we need to perserve the
245
        # behavior with the new system. To do so a new chain that is a copy of
246
        # the old one will be constructed, but only with the supplied methods
247
        # being added to the chain. This chain will be consulted for a value
248
        # and then thrown out. This is not efficient, nor is the methods arg
249
        # used in botocore, this is just for backwards compatibility.
250 11
        chain_builder = SubsetChainConfigFactory(session=self, methods=methods)
251 11
        mapping = create_botocore_default_config_mapping(self)
252 11
        for name, config_options in self.session_var_map.items():
253 11
            config_name, env_vars, default, typecast = config_options
254 11
            build_chain_config_args = {
255
                'conversion_func': typecast,
256
                'default': default,
257
            }
258 11
            if 'instance' in methods:
259 0
                build_chain_config_args['instance_name'] = name
260 11
            if 'env' in methods:
261 11
                build_chain_config_args['env_var_names'] = env_vars
262 11
            if 'config' in methods:
263 11
                build_chain_config_args['config_property_name'] = config_name
264 11
            mapping[name] = chain_builder.create_config_chain(
265
                **build_chain_config_args
266
            )
267 11
        config_store_component = ConfigValueStore(
268
            mapping=mapping
269
        )
270 11
        value = config_store_component.get_config_variable(logical_name)
271 11
        return value
272

273 11
    def set_config_variable(self, logical_name, value):
274
        """Set a configuration variable to a specific value.
275

276
        By using this method, you can override the normal lookup
277
        process used in ``get_config_variable`` by explicitly setting
278
        a value.  Subsequent calls to ``get_config_variable`` will
279
        use the ``value``.  This gives you per-session specific
280
        configuration values.
281

282
        ::
283
            >>> # Assume logical name 'foo' maps to env var 'FOO'
284
            >>> os.environ['FOO'] = 'myvalue'
285
            >>> s.get_config_variable('foo')
286
            'myvalue'
287
            >>> s.set_config_variable('foo', 'othervalue')
288
            >>> s.get_config_variable('foo')
289
            'othervalue'
290

291
        :type logical_name: str
292
        :param logical_name: The logical name of the session variable
293
            you want to set.  These are the keys in ``SESSION_VARIABLES``.
294
        :param value: The value to associate with the config variable.
295

296
        """
297 11
        logger.debug(
298
            "Setting config variable for %s to %r",
299
            logical_name,
300
            value,
301
        )
302 11
        self._session_instance_vars[logical_name] = value
303

304 11
    def instance_variables(self):
305 11
        return copy.copy(self._session_instance_vars)
306

307 11
    def get_scoped_config(self):
308
        """
309
        Returns the config values from the config file scoped to the current
310
        profile.
311

312
        The configuration data is loaded **only** from the config file.
313
        It does not resolve variables based on different locations
314
        (e.g. first from the session instance, then from environment
315
        variables, then from the config file).  If you want this lookup
316
        behavior, use the ``get_config_variable`` method instead.
317

318
        Note that this configuration is specific to a single profile (the
319
        ``profile`` session variable).
320

321
        If the ``profile`` session variable is set and the profile does
322
        not exist in the config file, a ``ProfileNotFound`` exception
323
        will be raised.
324

325
        :raises: ConfigNotFound, ConfigParseError, ProfileNotFound
326
        :rtype: dict
327

328
        """
329 11
        profile_name = self.get_config_variable('profile')
330 11
        profile_map = self._build_profile_map()
331
        # If a profile is not explicitly set return the default
332
        # profile config or an empty config dict if we don't have
333
        # a default profile.
334 11
        if profile_name is None:
335 11
            return profile_map.get('default', {})
336 11
        elif profile_name not in profile_map:
337
            # Otherwise if they specified a profile, it has to
338
            # exist (even if it's the default profile) otherwise
339
            # we complain.
340 11
            raise ProfileNotFound(profile=profile_name)
341
        else:
342 11
            return profile_map[profile_name]
343

344 11
    @property
345 2
    def full_config(self):
346
        """Return the parsed config file.
347

348
        The ``get_config`` method returns the config associated with the
349
        specified profile.  This property returns the contents of the
350
        **entire** config file.
351

352
        :rtype: dict
353
        """
354 11
        if self._config is None:
355 11
            try:
356 11
                config_file = self.get_config_variable('config_file')
357 11
                self._config = botocore.configloader.load_config(config_file)
358 11
            except ConfigNotFound:
359 11
                self._config = {'profiles': {}}
360 11
            try:
361
                # Now we need to inject the profiles from the
362
                # credentials file.  We don't actually need the values
363
                # in the creds file, only the profile names so that we
364
                # can validate the user is not referring to a nonexistent
365
                # profile.
366 11
                cred_file = self.get_config_variable('credentials_file')
367 11
                cred_profiles = botocore.configloader.raw_config_parse(
368
                    cred_file)
369 11
                for profile in cred_profiles:
370 11
                    cred_vars = cred_profiles[profile]
371 11
                    if profile not in self._config['profiles']:
372 11
                        self._config['profiles'][profile] = cred_vars
373
                    else:
374 0
                        self._config['profiles'][profile].update(cred_vars)
375 11
            except ConfigNotFound:
376 11
                pass
377 11
        return self._config
378

379 11
    def get_default_client_config(self):
380
        """Retrieves the default config for creating clients
381

382
        :rtype: botocore.client.Config
383
        :returns: The default client config object when creating clients. If
384
            the value is ``None`` then there is no default config object
385
            attached to the session.
386
        """
387 11
        return self._client_config
388

389 11
    def set_default_client_config(self, client_config):
390
        """Sets the default config for creating clients
391

392
        :type client_config: botocore.client.Config
393
        :param client_config: The default client config object when creating
394
            clients. If the value is ``None`` then there is no default config
395
            object attached to the session.
396
        """
397 11
        self._client_config = client_config
398

399 11
    def set_credentials(self, access_key, secret_key, token=None):
400
        """
401
        Manually create credentials for this session.  If you would
402
        prefer to use botocore without a config file, environment variables,
403
        or IAM roles, you can pass explicit credentials into this
404
        method to establish credentials for this session.
405

406
        :type access_key: str
407
        :param access_key: The access key part of the credentials.
408

409
        :type secret_key: str
410
        :param secret_key: The secret key part of the credentials.
411

412
        :type token: str
413
        :param token: An option session token used by STS session
414
            credentials.
415
        """
416 11
        self._credentials = botocore.credentials.Credentials(access_key,
417
                                                             secret_key,
418
                                                             token)
419

420 11
    def get_credentials(self):
421
        """
422
        Return the :class:`botocore.credential.Credential` object
423
        associated with this session.  If the credentials have not
424
        yet been loaded, this will attempt to load them.  If they
425
        have already been loaded, this will return the cached
426
        credentials.
427

428
        """
429 11
        if self._credentials is None:
430 11
            self._credentials = self._components.get_component(
431
                'credential_provider').load_credentials()
432 11
        return self._credentials
433

434 11
    def user_agent(self):
435
        """
436
        Return a string suitable for use as a User-Agent header.
437
        The string will be of the form:
438

439
        <agent_name>/<agent_version> Python/<py_ver> <plat_name>/<plat_ver> <exec_env>
440

441
        Where:
442

443
         - agent_name is the value of the `user_agent_name` attribute
444
           of the session object (`Botocore` by default).
445
         - agent_version is the value of the `user_agent_version`
446
           attribute of the session object (the botocore version by default).
447
           by default.
448
         - py_ver is the version of the Python interpreter beng used.
449
         - plat_name is the name of the platform (e.g. Darwin)
450
         - plat_ver is the version of the platform
451
         - exec_env is exec-env/$AWS_EXECUTION_ENV
452

453
        If ``user_agent_extra`` is not empty, then this value will be
454
        appended to the end of the user agent string.
455

456
        """
457 11
        base = '%s/%s Python/%s %s/%s' % (self.user_agent_name,
458
                                          self.user_agent_version,
459
                                          platform.python_version(),
460
                                          platform.system(),
461
                                          platform.release())
462 11
        if os.environ.get('AWS_EXECUTION_ENV') is not None:
463 11
            base += ' exec-env/%s' % os.environ.get('AWS_EXECUTION_ENV')
464 11
        if self.user_agent_extra:
465 11
            base += ' %s' % self.user_agent_extra
466

467 11
        return base
468

469 11
    def get_data(self, data_path):
470
        """
471
        Retrieve the data associated with `data_path`.
472

473
        :type data_path: str
474
        :param data_path: The path to the data you wish to retrieve.
475
        """
476 0
        return self.get_component('data_loader').load_data(data_path)
477

478 11
    def get_service_model(self, service_name, api_version=None):
479
        """Get the service model object.
480

481
        :type service_name: string
482
        :param service_name: The service name
483

484
        :type api_version: string
485
        :param api_version: The API version of the service.  If none is
486
            provided, then the latest API version will be used.
487

488
        :rtype: L{botocore.model.ServiceModel}
489
        :return: The botocore service model for the service.
490

491
        """
492 11
        service_description = self.get_service_data(service_name, api_version)
493 11
        return ServiceModel(service_description, service_name=service_name)
494

495 11
    def get_waiter_model(self, service_name, api_version=None):
496 11
        loader = self.get_component('data_loader')
497 11
        waiter_config = loader.load_service_model(
498
            service_name, 'waiters-2', api_version)
499 11
        return waiter.WaiterModel(waiter_config)
500

501 11
    def get_paginator_model(self, service_name, api_version=None):
502 11
        loader = self.get_component('data_loader')
503 11
        paginator_config = loader.load_service_model(
504
            service_name, 'paginators-1', api_version)
505 11
        return paginate.PaginatorModel(paginator_config)
506

507 11
    def get_service_data(self, service_name, api_version=None):
508
        """
509
        Retrieve the fully merged data associated with a service.
510
        """
511 11
        data_path = service_name
512 11
        service_data = self.get_component('data_loader').load_service_model(
513
            data_path,
514
            type_name='service-2',
515
            api_version=api_version
516
        )
517 11
        service_id = EVENT_ALIASES.get(service_name, service_name)
518 11
        self._events.emit('service-data-loaded.%s' % service_id,
519
                          service_data=service_data,
520
                          service_name=service_name, session=self)
521 11
        return service_data
522

523 11
    def get_available_services(self):
524
        """
525
        Return a list of names of available services.
526
        """
527 11
        return self.get_component('data_loader')\
528
            .list_available_services(type_name='service-2')
529

530 11
    def set_debug_logger(self, logger_name='botocore'):
531
        """
532
        Convenience function to quickly configure full debug output
533
        to go to the console.
534
        """
535 11
        self.set_stream_logger(logger_name, logging.DEBUG)
536

537 11
    def set_stream_logger(self, logger_name, log_level, stream=None,
538
                          format_string=None):
539
        """
540
        Convenience method to configure a stream logger.
541

542
        :type logger_name: str
543
        :param logger_name: The name of the logger to configure
544

545
        :type log_level: str
546
        :param log_level: The log level to set for the logger.  This
547
            is any param supported by the ``.setLevel()`` method of
548
            a ``Log`` object.
549

550
        :type stream: file
551
        :param stream: A file like object to log to.  If none is provided
552
            then sys.stderr will be used.
553

554
        :type format_string: str
555
        :param format_string: The format string to use for the log
556
            formatter.  If none is provided this will default to
557
            ``self.LOG_FORMAT``.
558

559
        """
560 11
        log = logging.getLogger(logger_name)
561 11
        log.setLevel(logging.DEBUG)
562

563 11
        ch = logging.StreamHandler(stream)
564 11
        ch.setLevel(log_level)
565

566
        # create formatter
567 11
        if format_string is None:
568 11
            format_string = self.LOG_FORMAT
569 11
        formatter = logging.Formatter(format_string)
570

571
        # add formatter to ch
572 11
        ch.setFormatter(formatter)
573

574
        # add ch to logger
575 11
        log.addHandler(ch)
576

577 11
    def set_file_logger(self, log_level, path, logger_name='botocore'):
578
        """
579
        Convenience function to quickly configure any level of logging
580
        to a file.
581

582
        :type log_level: int
583
        :param log_level: A log level as specified in the `logging` module
584

585
        :type path: string
586
        :param path: Path to the log file.  The file will be created
587
            if it doesn't already exist.
588
        """
589 11
        log = logging.getLogger(logger_name)
590 11
        log.setLevel(logging.DEBUG)
591

592
        # create console handler and set level to debug
593 11
        ch = logging.FileHandler(path)
594 11
        ch.setLevel(log_level)
595

596
        # create formatter
597 11
        formatter = logging.Formatter(self.LOG_FORMAT)
598

599
        # add formatter to ch
600 11
        ch.setFormatter(formatter)
601

602
        # add ch to logger
603 11
        log.addHandler(ch)
604

605 11
    def register(self, event_name, handler, unique_id=None,
606
                 unique_id_uses_count=False):
607
        """Register a handler with an event.
608

609
        :type event_name: str
610
        :param event_name: The name of the event.
611

612
        :type handler: callable
613
        :param handler: The callback to invoke when the event
614
            is emitted.  This object must be callable, and must
615
            accept ``**kwargs``.  If either of these preconditions are
616
            not met, a ``ValueError`` will be raised.
617

618
        :type unique_id: str
619
        :param unique_id: An optional identifier to associate with the
620
            registration.  A unique_id can only be used once for
621
            the entire session registration (unless it is unregistered).
622
            This can be used to prevent an event handler from being
623
            registered twice.
624

625
        :param unique_id_uses_count: boolean
626
        :param unique_id_uses_count: Specifies if the event should maintain
627
            a count when a ``unique_id`` is registered and unregisted. The
628
            event can only be completely unregistered once every register call
629
            using the unique id has been matched by an ``unregister`` call.
630
            If ``unique_id`` is specified, subsequent ``register``
631
            calls must use the same value for  ``unique_id_uses_count``
632
            as the ``register`` call that first registered the event.
633

634
        :raises ValueError: If the call to ``register`` uses ``unique_id``
635
            but the value for ``unique_id_uses_count`` differs from the
636
            ``unique_id_uses_count`` value declared by the very first
637
            ``register`` call for that ``unique_id``.
638
        """
639 11
        self._events.register(event_name, handler, unique_id,
640
                              unique_id_uses_count=unique_id_uses_count)
641

642 11
    def unregister(self, event_name, handler=None, unique_id=None,
643
                   unique_id_uses_count=False):
644
        """Unregister a handler with an event.
645

646
        :type event_name: str
647
        :param event_name: The name of the event.
648

649
        :type handler: callable
650
        :param handler: The callback to unregister.
651

652
        :type unique_id: str
653
        :param unique_id: A unique identifier identifying the callback
654
            to unregister.  You can provide either the handler or the
655
            unique_id, you do not have to provide both.
656

657
        :param unique_id_uses_count: boolean
658
        :param unique_id_uses_count: Specifies if the event should maintain
659
            a count when a ``unique_id`` is registered and unregisted. The
660
            event can only be completely unregistered once every ``register``
661
            call using the ``unique_id`` has been matched by an ``unregister``
662
            call. If the ``unique_id`` is specified, subsequent
663
            ``unregister`` calls must use the same value for
664
            ``unique_id_uses_count`` as the ``register`` call that first
665
            registered the event.
666

667
        :raises ValueError: If the call to ``unregister`` uses ``unique_id``
668
            but the value for ``unique_id_uses_count`` differs from the
669
            ``unique_id_uses_count`` value declared by the very first
670
            ``register`` call for that ``unique_id``.
671
        """
672 11
        self._events.unregister(event_name, handler=handler,
673
                                unique_id=unique_id,
674
                                unique_id_uses_count=unique_id_uses_count)
675

676 11
    def emit(self, event_name, **kwargs):
677 11
        return self._events.emit(event_name, **kwargs)
678

679 11
    def emit_first_non_none_response(self, event_name, **kwargs):
680 11
        responses = self._events.emit(event_name, **kwargs)
681 11
        return first_non_none_response(responses)
682

683 11
    def get_component(self, name):
684 11
        try:
685 11
            return self._components.get_component(name)
686 11
        except ValueError:
687 11
            if name in ['endpoint_resolver', 'exceptions_factory']:
688 11
                warnings.warn(
689
                    'Fetching the %s component with the get_component() '
690
                    'method is deprecated as the component has always been '
691
                    'considered an internal interface of botocore' % name,
692
                    DeprecationWarning)
693 11
                return self._internal_components.get_component(name)
694 11
            raise
695

696 11
    def _get_internal_component(self, name):
697
        # While this method may be called by botocore classes outside of the
698
        # Session, this method should **never** be used by a class that lives
699
        # outside of botocore.
700 11
        return self._internal_components.get_component(name)
701

702 11
    def _register_internal_component(self, name, component):
703
        # While this method may be called by botocore classes outside of the
704
        # Session, this method should **never** be used by a class that lives
705
        # outside of botocore.
706 11
        return self._internal_components.register_component(name, component)
707

708 11
    def register_component(self, name, component):
709 11
        self._components.register_component(name, component)
710

711 11
    def lazy_register_component(self, name, component):
712 0
        self._components.lazy_register_component(name, component)
713

714 11
    def create_client(self, service_name, region_name=None, api_version=None,
715
                      use_ssl=True, verify=None, endpoint_url=None,
716
                      aws_access_key_id=None, aws_secret_access_key=None,
717
                      aws_session_token=None, config=None):
718
        """Create a botocore client.
719

720
        :type service_name: string
721
        :param service_name: The name of the service for which a client will
722
            be created.  You can use the ``Sesssion.get_available_services()``
723
            method to get a list of all available service names.
724

725
        :type region_name: string
726
        :param region_name: The name of the region associated with the client.
727
            A client is associated with a single region.
728

729
        :type api_version: string
730
        :param api_version: The API version to use.  By default, botocore will
731
            use the latest API version when creating a client.  You only need
732
            to specify this parameter if you want to use a previous API version
733
            of the client.
734

735
        :type use_ssl: boolean
736
        :param use_ssl: Whether or not to use SSL.  By default, SSL is used.
737
            Note that not all services support non-ssl connections.
738

739
        :type verify: boolean/string
740
        :param verify: Whether or not to verify SSL certificates.
741
            By default SSL certificates are verified.  You can provide the
742
            following values:
743

744
            * False - do not validate SSL certificates.  SSL will still be
745
              used (unless use_ssl is False), but SSL certificates
746
              will not be verified.
747
            * path/to/cert/bundle.pem - A filename of the CA cert bundle to
748
              uses.  You can specify this argument if you want to use a
749
              different CA cert bundle than the one used by botocore.
750

751
        :type endpoint_url: string
752
        :param endpoint_url: The complete URL to use for the constructed
753
            client.  Normally, botocore will automatically construct the
754
            appropriate URL to use when communicating with a service.  You can
755
            specify a complete URL (including the "http/https" scheme) to
756
            override this behavior.  If this value is provided, then
757
            ``use_ssl`` is ignored.
758

759
        :type aws_access_key_id: string
760
        :param aws_access_key_id: The access key to use when creating
761
            the client.  This is entirely optional, and if not provided,
762
            the credentials configured for the session will automatically
763
            be used.  You only need to provide this argument if you want
764
            to override the credentials used for this specific client.
765

766
        :type aws_secret_access_key: string
767
        :param aws_secret_access_key: The secret key to use when creating
768
            the client.  Same semantics as aws_access_key_id above.
769

770
        :type aws_session_token: string
771
        :param aws_session_token: The session token to use when creating
772
            the client.  Same semantics as aws_access_key_id above.
773

774
        :type config: botocore.client.Config
775
        :param config: Advanced client configuration options. If a value
776
            is specified in the client config, its value will take precedence
777
            over environment variables and configuration values, but not over
778
            a value passed explicitly to the method. If a default config
779
            object is set on the session, the config object used when creating
780
            the client will be the result of calling ``merge()`` on the
781
            default config with the config provided to this call.
782

783
        :rtype: botocore.client.BaseClient
784
        :return: A botocore client instance
785

786
        """
787 11
        default_client_config = self.get_default_client_config()
788
        # If a config is provided and a default config is set, then
789
        # use the config resulting from merging the two.
790 11
        if config is not None and default_client_config is not None:
791 11
            config = default_client_config.merge(config)
792
        # If a config was not provided then use the default
793
        # client config from the session
794 11
        elif default_client_config is not None:
795 11
            config = default_client_config
796

797 11
        region_name = self._resolve_region_name(region_name, config)
798

799
        # Figure out the verify value base on the various
800
        # configuration options.
801 11
        if verify is None:
802 11
            verify = self.get_config_variable('ca_bundle')
803

804 11
        if api_version is None:
805 11
            api_version = self.get_config_variable('api_versions').get(
806
                service_name, None)
807

808 11
        loader = self.get_component('data_loader')
809 11
        event_emitter = self.get_component('event_emitter')
810 11
        response_parser_factory = self.get_component(
811
            'response_parser_factory')
812 11
        if config is not None and config.signature_version is UNSIGNED:
813 11
            credentials = None
814 11
        elif aws_access_key_id is not None and aws_secret_access_key is not None:
815 11
            credentials = botocore.credentials.Credentials(
816
                access_key=aws_access_key_id,
817
                secret_key=aws_secret_access_key,
818
                token=aws_session_token)
819 11
        elif self._missing_cred_vars(aws_access_key_id,
820
                                     aws_secret_access_key):
821 11
            raise PartialCredentialsError(
822
                provider='explicit',
823
                cred_var=self._missing_cred_vars(aws_access_key_id,
824
                                                 aws_secret_access_key))
825
        else:
826 11
            credentials = self.get_credentials()
827 11
        endpoint_resolver = self._get_internal_component('endpoint_resolver')
828 11
        exceptions_factory = self._get_internal_component('exceptions_factory')
829 11
        config_store = self.get_component('config_store')
830 11
        client_creator = botocore.client.ClientCreator(
831
            loader, endpoint_resolver, self.user_agent(), event_emitter,
832
            retryhandler, translate, response_parser_factory,
833
            exceptions_factory, config_store)
834 11
        client = client_creator.create_client(
835
            service_name=service_name, region_name=region_name,
836
            is_secure=use_ssl, endpoint_url=endpoint_url, verify=verify,
837
            credentials=credentials, scoped_config=self.get_scoped_config(),
838
            client_config=config, api_version=api_version)
839 11
        monitor = self._get_internal_component('monitor')
840 11
        if monitor is not None:
841 11
            monitor.register(client.meta.events)
842 11
        return client
843

844 11
    def _resolve_region_name(self, region_name, config):
845
        # Figure out the user-provided region based on the various
846
        # configuration options.
847 11
        if region_name is None:
848 11
            if config and config.region_name is not None:
849 11
                region_name = config.region_name
850
            else:
851 11
                region_name = self.get_config_variable('region')
852

853 11
        validate_region_name(region_name)
854
        # For any client that we create in retrieving credentials
855
        # we want to create it using the same region as specified in
856
        # creating this client. It is important to note though that the
857
        # credentials client is only created once per session. So if a new
858
        # client is created with a different region, its credential resolver
859
        # will use the region of the first client. However, that is not an
860
        # issue as of now because the credential resolver uses only STS and
861
        # the credentials returned at regional endpoints are valid across
862
        # all regions in the partition.
863 11
        self._last_client_region_used = region_name
864 11
        return region_name
865

866 11
    def _missing_cred_vars(self, access_key, secret_key):
867 11
        if access_key is not None and secret_key is None:
868 11
            return 'aws_secret_access_key'
869 11
        if secret_key is not None and access_key is None:
870 11
            return 'aws_access_key_id'
871 11
        return None
872

873 11
    def get_available_partitions(self):
874
        """Lists the available partitions found on disk
875

876
        :rtype: list
877
        :return: Returns a list of partition names (e.g., ["aws", "aws-cn"])
878
        """
879 11
        resolver = self._get_internal_component('endpoint_resolver')
880 11
        return resolver.get_available_partitions()
881

882 11
    def get_available_regions(self, service_name, partition_name='aws',
883
                              allow_non_regional=False):
884
        """Lists the region and endpoint names of a particular partition.
885

886
        :type service_name: string
887
        :param service_name: Name of a service to list endpoint for (e.g., s3).
888
            This parameter accepts a service name (e.g., "elb") or endpoint
889
            prefix (e.g., "elasticloadbalancing").
890

891
        :type partition_name: string
892
        :param partition_name: Name of the partition to limit endpoints to.
893
            (e.g., aws for the public AWS endpoints, aws-cn for AWS China
894
            endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.
895

896
        :type allow_non_regional: bool
897
        :param allow_non_regional: Set to True to include endpoints that are
898
             not regional endpoints (e.g., s3-external-1,
899
             fips-us-gov-west-1, etc).
900
        :return: Returns a list of endpoint names (e.g., ["us-east-1"]).
901
        """
902 11
        resolver = self._get_internal_component('endpoint_resolver')
903 11
        results = []
904 11
        try:
905 11
            service_data = self.get_service_data(service_name)
906 11
            endpoint_prefix = service_data['metadata'].get(
907
                'endpointPrefix', service_name)
908 11
            results = resolver.get_available_endpoints(
909
                endpoint_prefix, partition_name, allow_non_regional)
910 11
        except UnknownServiceError:
911 11
            pass
912 11
        return results
913

914

915 11
class ComponentLocator(object):
916
    """Service locator for session components."""
917 11
    def __init__(self):
918 11
        self._components = {}
919 11
        self._deferred = {}
920

921 11
    def get_component(self, name):
922 11
        if name in self._deferred:
923 11
            factory = self._deferred[name]
924 11
            self._components[name] = factory()
925
            # Only delete the component from the deferred dict after
926
            # successfully creating the object from the factory as well as
927
            # injecting the instantiated value into the _components dict.
928 11
            del self._deferred[name]
929 11
        try:
930 11
            return self._components[name]
931 11
        except KeyError:
932 11
            raise ValueError("Unknown component: %s" % name)
933

934 11
    def register_component(self, name, component):
935 11
        self._components[name] = component
936 11
        try:
937 11
            del self._deferred[name]
938 11
        except KeyError:
939 11
            pass
940

941 11
    def lazy_register_component(self, name, no_arg_factory):
942 11
        self._deferred[name] = no_arg_factory
943 11
        try:
944 11
            del self._components[name]
945 11
        except KeyError:
946 11
            pass
947

948

949 11
class SessionVarDict(MutableMapping):
950 11
    def __init__(self, session, session_vars):
951 11
        self._session = session
952 11
        self._store = copy.copy(session_vars)
953

954 11
    def __getitem__(self, key):
955 11
        return self._store[key]
956

957 11
    def __setitem__(self, key, value):
958 11
        self._store[key] = value
959 11
        self._update_config_store_from_session_vars(key, value)
960

961 11
    def __delitem__(self, key):
962 0
        del self._store[key]
963

964 11
    def __iter__(self):
965 11
        return iter(self._store)
966

967 11
    def __len__(self):
968 0
        return len(self._store)
969

970 11
    def _update_config_store_from_session_vars(self, logical_name,
971
                                               config_options):
972
        # This is for backwards compatibility. The new preferred way to
973
        # modify configuration logic is to use the component system to get
974
        # the config_store component from the session, and then update
975
        # a key with a custom config provider(s).
976
        # This backwards compatibility method takes the old session_vars
977
        # list of tuples and and transforms that into a set of updates to
978
        # the config_store component.
979 11
        config_chain_builder = ConfigChainFactory(session=self._session)
980 11
        config_name, env_vars, default, typecast = config_options
981 11
        config_store = self._session.get_component('config_store')
982 11
        config_store.set_config_provider(
983
            logical_name,
984
            config_chain_builder.create_config_chain(
985
                instance_name=logical_name,
986
                env_var_names=env_vars,
987
                config_property_names=config_name,
988
                default=default,
989
                conversion_func=typecast,
990
            )
991
        )
992

993

994 11
class SubsetChainConfigFactory(object):
995
    """A class for creating backwards compatible configuration chains.
996

997
    This class can be used instead of
998
    :class:`botocore.configprovider.ConfigChainFactory` to make it honor the
999
    methods argument to get_config_variable. This class can be used to filter
1000
    out providers that are not in the methods tuple when creating a new config
1001
    chain.
1002
    """
1003 11
    def __init__(self, session, methods, environ=None):
1004 11
        self._factory = ConfigChainFactory(session, environ)
1005 11
        self._supported_methods = methods
1006

1007 11
    def create_config_chain(self, instance_name=None, env_var_names=None,
1008
                            config_property_name=None, default=None,
1009
                            conversion_func=None):
1010
        """Build a config chain following the standard botocore pattern.
1011

1012
        This config chain factory will omit any providers not in the methods
1013
        tuple provided at initialization. For example if given the tuple
1014
        ('instance', 'config',) it will not inject the environment provider
1015
        into the standard config chain. This lets the botocore session support
1016
        the custom ``methods`` argument for all the default botocore config
1017
        variables when calling ``get_config_variable``.
1018
        """
1019 11
        if 'instance' not in self._supported_methods:
1020 11
            instance_name = None
1021 11
        if 'env' not in self._supported_methods:
1022 0
            env_var_names = None
1023 11
        if 'config' not in self._supported_methods:
1024 11
            config_property_name = None
1025 11
        return self._factory.create_config_chain(
1026
            instance_name=instance_name,
1027
            env_var_names=env_var_names,
1028
            config_property_names=config_property_name,
1029
            default=default,
1030
            conversion_func=conversion_func,
1031
        )
1032

1033

1034 11
def get_session(env_vars=None):
1035
    """
1036
    Return a new session object.
1037
    """
1038 11
    return Session(env_vars)

Read our documentation on viewing source code .

Loading