session.py 43 KB

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