[Python-modules-commits] [python-botocore] 01/07: Import python-botocore_1.3.23.orig.tar.gz

Takaki Taniguchi takaki at moszumanska.debian.org
Tue Feb 2 09:07:49 UTC 2016


This is an automated email from the git hooks/post-receive script.

takaki pushed a commit to branch master
in repository python-botocore.

commit b50932b9a1069998d6bf7db71500684489cffc30
Author: TANIGUCHI Takaki <takaki at asis.media-as.org>
Date:   Tue Feb 2 17:54:52 2016 +0900

    Import python-botocore_1.3.23.orig.tar.gz
---
 PKG-INFO                                           |    2 +-
 botocore.egg-info/PKG-INFO                         |    2 +-
 botocore.egg-info/SOURCES.txt                      |   21 +
 botocore/__init__.py                               |    2 +-
 botocore/client.py                                 |  135 +-
 botocore/compat.py                                 |    2 +
 botocore/credentials.py                            |  279 +-
 botocore/data/_endpoints.json                      |    7 +-
 botocore/data/_retry.json                          |   10 +-
 botocore/data/acm/2015-12-08/service-2.json        |  585 +++
 .../data/autoscaling/2011-01-01/service-2.json     | 1051 +----
 .../data/cloudformation/2010-05-15/service-2.json  |  257 +-
 .../data/cloudfront/2015-09-17/paginators-1.json   |   32 +
 botocore/data/cloudfront/2015-09-17/service-2.json | 2928 ++++++++++++
 botocore/data/cloudfront/2015-09-17/waiters-2.json |   47 +
 .../data/cloudfront/2016-01-13/paginators-1.json   |   32 +
 botocore/data/cloudfront/2016-01-13/service-2.json | 3021 +++++++++++++
 botocore/data/cloudfront/2016-01-13/waiters-2.json |   47 +
 .../data/cloudfront/2016-01-28/paginators-1.json   |   32 +
 botocore/data/cloudfront/2016-01-28/service-2.json | 3022 +++++++++++++
 botocore/data/cloudfront/2016-01-28/waiters-2.json |   47 +
 botocore/data/cloudtrail/2013-11-01/service-2.json | 1311 +-----
 botocore/data/config/2014-11-12/service-2.json     |  608 +--
 botocore/data/devicefarm/2015-06-23/service-2.json |   28 +-
 .../data/directconnect/2012-10-25/service-2.json   |  453 +-
 botocore/data/ds/2015-04-16/service-2.json         | 1151 +++--
 botocore/data/ec2/2015-10-01/service-2.json        | 1583 ++++++-
 botocore/data/ecr/2015-09-21/service-2.json        | 1251 ++++++
 botocore/data/ecs/2014-11-13/service-2.json        |   48 +-
 botocore/data/emr/2009-03-31/service-2.json        |  407 +-
 botocore/data/events/2014-02-03/service-2.json     |  997 +++++
 botocore/data/iam/2010-05-08/service-2.json        | 3958 ++---------------
 botocore/data/iot-data/2015-05-28/service-2.json   |  321 +-
 botocore/data/iot/2015-05-28/service-2.json        |  196 +-
 botocore/data/kms/2014-11-01/paginators-1.json     |   32 +
 botocore/data/logs/2014-03-28/service-2.json       |  707 +--
 botocore/data/opsworks/2013-02-18/service-2.json   | 1044 +----
 botocore/data/rds/2014-10-31/service-2.json        | 4660 ++++----------------
 botocore/data/route53/2013-04-01/service-2.json    | 2950 ++++++++-----
 botocore/data/sqs/2012-11-05/examples-1.json       |   44 +
 botocore/data/ssm/2014-11-06/paginators-1.json     |   28 +
 botocore/data/ssm/2014-11-06/service-2.json        | 1012 +----
 botocore/data/sts/2011-06-15/service-2.json        |  310 +-
 botocore/data/waf/2015-08-24/service-2.json        | 1341 +++---
 botocore/docs/bcdoc/restdoc.py                     |    1 +
 botocore/docs/client.py                            |   15 +-
 botocore/docs/example.py                           |    2 +
 botocore/docs/method.py                            |    8 +-
 botocore/docs/params.py                            |    5 +-
 botocore/docs/service.py                           |   14 +-
 botocore/docs/shape.py                             |    4 +-
 botocore/docs/sharedexample.py                     |  221 +
 botocore/exceptions.py                             |   19 +
 botocore/handlers.py                               |  136 +-
 botocore/loaders.py                                |   28 +-
 botocore/model.py                                  |   18 +-
 botocore/parsers.py                                |    9 +
 botocore/response.py                               |    5 +
 botocore/session.py                                |   38 +-
 botocore/signers.py                                |  131 +-
 botocore/utils.py                                  |   21 +
 botocore/validate.py                               |    2 +-
 docs/source/conf.py                                |    4 +-
 docs/source/reference/response.rst                 |   11 +
 docs/source/topics/events.rst                      |    2 +-
 requirements.txt                                   |    2 +-
 tests/__init__.py                                  |    2 -
 tests/acceptance/features/environment.py           |   12 +-
 tests/acceptance/features/smoke/acm/acm.feature    |   12 +
 .../features/smoke/autoscaling/autoscaling.feature |    5 +-
 .../smoke/cloudformation/cloudformation.feature    |    5 +-
 .../features/smoke/cloudfront/cloudfront.feature   |    5 +-
 .../features/smoke/cloudhsm/cloudhsm.feature       |    5 +-
 .../features/smoke/cloudsearch/cloudsearch.feature |    5 +-
 .../features/smoke/cloudtrail/cloudtrail.feature   |    5 +-
 .../features/smoke/cloudwatch/cloudwatch.feature   |    5 +-
 .../smoke/cloudwatchlogs/cloudwatchlogs.feature    |    5 +-
 .../features/smoke/codecommit/codecommit.feature   |    5 +-
 .../features/smoke/codedeploy/codedeploy.feature   |    5 +-
 .../smoke/codepipeline/codepipeline.feature        |    5 +-
 .../smoke/cognitoidentity/cognitoidentity.feature  |    5 +-
 .../features/smoke/cognitosync/cognitosync.feature |    5 +-
 .../smoke/configservice/configservice.feature      |    5 +-
 .../smoke/datapipeline/datapipeline.feature        |    5 +-
 .../features/smoke/devicefarm/devicefarm.feature   |    5 +-
 .../smoke/directconnect/directconnect.feature      |    5 +-
 .../features/smoke/dynamodb/dynamodb.feature       |    5 +-
 .../smoke/dynamodbstreams/dynamodbstreams.feature  |    5 +-
 tests/acceptance/features/smoke/ec2/ec2.feature    |    5 +-
 .../features/smoke/elasticache/elasticache.feature |    5 +-
 .../elasticbeanstalk/elasticbeanstalk.feature      |    5 +-
 .../elasticloadbalancing.feature                   |    5 +-
 .../elastictranscoder/elastictranscoder.feature    |    5 +-
 tests/acceptance/features/smoke/emr/emr.feature    |    5 +-
 tests/acceptance/features/smoke/es/es.feature      |    5 +-
 .../features/smoke/glacier/glacier.feature         |    5 +-
 tests/acceptance/features/smoke/iam/iam.feature    |    5 +-
 .../smoke/importexport/importexport.feature        |    5 +-
 .../features/smoke/kinesis/kinesis.feature         |    5 +-
 tests/acceptance/features/smoke/kms/kms.feature    |    5 +-
 .../features/smoke/lambda/lambda.feature           |    5 +-
 .../smoke/machinelearning/machinelearning.feature  |    5 +-
 .../features/smoke/opsworks/opsworks.feature       |    5 +-
 tests/acceptance/features/smoke/rds/rds.feature    |    5 +-
 .../features/smoke/redshift/redshift.feature       |    5 +-
 .../features/smoke/route53/route53.feature         |    5 +-
 .../smoke/route53domains/route53domains.feature    |    5 +-
 tests/acceptance/features/smoke/ses/ses.feature    |    5 +-
 tests/acceptance/features/smoke/sns/sns.feature    |    5 +-
 tests/acceptance/features/smoke/sqs/sqs.feature    |    5 +-
 tests/acceptance/features/smoke/ssm/ssm.feature    |    5 +-
 .../smoke/storagegateway/storagegateway.feature    |    5 +-
 tests/acceptance/features/smoke/sts/sts.feature    |    5 +-
 .../features/smoke/support/support.feature         |    5 +-
 tests/acceptance/features/smoke/swf/swf.feature    |    5 +-
 tests/acceptance/features/smoke/waf/waf.feature    |    5 +-
 .../features/smoke/workspaces/workspaces.feature   |    5 +-
 tests/acceptance/features/steps/base.py            |   15 +-
 tests/functional/docs/__init__.py                  |    8 +-
 tests/functional/docs/test_s3.py                   |   15 +
 tests/functional/docs/test_streaming_body.py       |   36 +
 tests/functional/test_loaders.py                   |   40 +
 tests/integration/test_s3.py                       |   43 +
 tests/unit/docs/__init__.py                        |   24 +
 tests/unit/docs/test_method.py                     |   13 +
 tests/unit/docs/test_service.py                    |    4 +
 tests/unit/docs/test_sharedexample.py              |  270 ++
 tests/unit/protocols/output/rest-xml.json          |   68 +
 .../xml/responses/s3-get-bucket-acl.json           |    5 +-
 .../xml/responses/s3-get-bucket-logging.json       |    5 +-
 tests/unit/test_client.py                          |  107 +-
 tests/unit/test_credentials.py                     |  342 +-
 tests/unit/test_handlers.py                        |  134 +-
 tests/unit/test_loaders.py                         |   19 +-
 tests/unit/test_s3_addressing.py                   |   11 +-
 tests/unit/test_session.py                         |   45 +-
 tests/unit/test_signers.py                         |   81 +-
 tests/unit/test_utils.py                           |   13 +
 tests/unit/test_waiters.py                         |    4 +-
 139 files changed, 22082 insertions(+), 16183 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index de1ba46..43c9235 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: botocore
-Version: 1.3.9
+Version: 1.3.23
 Summary: Low-level, data-driven core of boto 3.
 Home-page: https://github.com/boto/botocore
 Author: Amazon Web Services
diff --git a/botocore.egg-info/PKG-INFO b/botocore.egg-info/PKG-INFO
index de1ba46..43c9235 100644
--- a/botocore.egg-info/PKG-INFO
+++ b/botocore.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: botocore
-Version: 1.3.9
+Version: 1.3.23
 Summary: Low-level, data-driven core of boto 3.
 Home-page: https://github.com/boto/botocore
 Author: Amazon Web Services
diff --git a/botocore.egg-info/SOURCES.txt b/botocore.egg-info/SOURCES.txt
index 2e25267..859c5cd 100644
--- a/botocore.egg-info/SOURCES.txt
+++ b/botocore.egg-info/SOURCES.txt
@@ -36,6 +36,7 @@ botocore.egg-info/requires.txt
 botocore.egg-info/top_level.txt
 botocore/data/_endpoints.json
 botocore/data/_retry.json
+botocore/data/acm/2015-12-08/service-2.json
 botocore/data/apigateway/2015-07-09/paginators-1.json
 botocore/data/apigateway/2015-07-09/service-2.json
 botocore/data/autoscaling/2011-01-01/paginators-1.json
@@ -57,6 +58,15 @@ botocore/data/cloudfront/2015-04-17/waiters-2.json
 botocore/data/cloudfront/2015-07-27/paginators-1.json
 botocore/data/cloudfront/2015-07-27/service-2.json
 botocore/data/cloudfront/2015-07-27/waiters-2.json
+botocore/data/cloudfront/2015-09-17/paginators-1.json
+botocore/data/cloudfront/2015-09-17/service-2.json
+botocore/data/cloudfront/2015-09-17/waiters-2.json
+botocore/data/cloudfront/2016-01-13/paginators-1.json
+botocore/data/cloudfront/2016-01-13/service-2.json
+botocore/data/cloudfront/2016-01-13/waiters-2.json
+botocore/data/cloudfront/2016-01-28/paginators-1.json
+botocore/data/cloudfront/2016-01-28/service-2.json
+botocore/data/cloudfront/2016-01-28/waiters-2.json
 botocore/data/cloudhsm/2014-05-30/service-2.json
 botocore/data/cloudsearch/2011-02-01/service-2.json
 botocore/data/cloudsearch/2013-01-01/service-2.json
@@ -94,6 +104,7 @@ botocore/data/ec2/2015-04-15/waiters-2.json
 botocore/data/ec2/2015-10-01/paginators-1.json
 botocore/data/ec2/2015-10-01/service-2.json
 botocore/data/ec2/2015-10-01/waiters-2.json
+botocore/data/ecr/2015-09-21/service-2.json
 botocore/data/ecs/2014-11-13/paginators-1.json
 botocore/data/ecs/2014-11-13/service-2.json
 botocore/data/ecs/2014-11-13/waiters-2.json
@@ -115,6 +126,7 @@ botocore/data/emr/2009-03-31/paginators-1.json
 botocore/data/emr/2009-03-31/service-2.json
 botocore/data/emr/2009-03-31/waiters-2.json
 botocore/data/es/2015-01-01/service-2.json
+botocore/data/events/2014-02-03/service-2.json
 botocore/data/firehose/2015-08-04/service-2.json
 botocore/data/glacier/2012-06-01/paginators-1.json
 botocore/data/glacier/2012-06-01/service-2.json
@@ -129,6 +141,7 @@ botocore/data/iot/2015-05-28/service-2.json
 botocore/data/kinesis/2013-12-02/paginators-1.json
 botocore/data/kinesis/2013-12-02/service-2.json
 botocore/data/kinesis/2013-12-02/waiters-2.json
+botocore/data/kms/2014-11-01/paginators-1.json
 botocore/data/kms/2014-11-01/service-2.json
 botocore/data/lambda/2014-11-11/service-2.json
 botocore/data/lambda/2015-03-31/paginators-1.json
@@ -164,7 +177,9 @@ botocore/data/ses/2010-12-01/service-2.json
 botocore/data/ses/2010-12-01/waiters-2.json
 botocore/data/sns/2010-03-31/paginators-1.json
 botocore/data/sns/2010-03-31/service-2.json
+botocore/data/sqs/2012-11-05/examples-1.json
 botocore/data/sqs/2012-11-05/service-2.json
+botocore/data/ssm/2014-11-06/paginators-1.json
 botocore/data/ssm/2014-11-06/service-2.json
 botocore/data/storagegateway/2013-06-30/paginators-1.json
 botocore/data/storagegateway/2013-06-30/service-2.json
@@ -184,6 +199,7 @@ botocore/docs/paginator.py
 botocore/docs/params.py
 botocore/docs/service.py
 botocore/docs/shape.py
+botocore/docs/sharedexample.py
 botocore/docs/utils.py
 botocore/docs/waiter.py
 botocore/docs/bcdoc/__init__.py
@@ -284,6 +300,7 @@ docs/source/development/designnotes.rst
 docs/source/development/index.rst
 docs/source/reference/index.rst
 docs/source/reference/loaders.rst
+docs/source/reference/response.rst
 docs/source/topics/events.rst
 docs/source/topics/index.rst
 docs/source/topics/paginators.rst
@@ -291,6 +308,7 @@ docs/source/tutorial/index.rst
 tests/__init__.py
 tests/cmd-runner
 tests/acceptance/features/environment.py
+tests/acceptance/features/smoke/acm/acm.feature
 tests/acceptance/features/smoke/autoscaling/autoscaling.feature
 tests/acceptance/features/smoke/cloudformation/cloudformation.feature
 tests/acceptance/features/smoke/cloudfront/cloudfront.feature
@@ -347,6 +365,7 @@ tests/functional/__init__.py
 tests/functional/test_client_class_names.py
 tests/functional/test_cloudsearchdomain.py
 tests/functional/test_iot_data.py
+tests/functional/test_loaders.py
 tests/functional/test_machinelearning.py
 tests/functional/test_model_completeness.py
 tests/functional/test_public_apis.py
@@ -358,6 +377,7 @@ tests/functional/docs/test_autoscaling.py
 tests/functional/docs/test_ec2.py
 tests/functional/docs/test_glacier.py
 tests/functional/docs/test_s3.py
+tests/functional/docs/test_streaming_body.py
 tests/functional/leak/__init__.py
 tests/functional/leak/test_resource_leaks.py
 tests/integration/__init__.py
@@ -589,6 +609,7 @@ tests/unit/docs/test_method.py
 tests/unit/docs/test_paginator.py
 tests/unit/docs/test_params.py
 tests/unit/docs/test_service.py
+tests/unit/docs/test_sharedexample.py
 tests/unit/docs/test_utils.py
 tests/unit/docs/test_waiter.py
 tests/unit/docs/bcdoc/__init__.py
diff --git a/botocore/__init__.py b/botocore/__init__.py
index c48a20e..5104736 100644
--- a/botocore/__init__.py
+++ b/botocore/__init__.py
@@ -16,7 +16,7 @@ import os
 import re
 import logging
 
-__version__ = '1.3.9'
+__version__ = '1.3.23'
 
 
 class NullHandler(logging.Handler):
diff --git a/botocore/client.py b/botocore/client.py
index 203e823..96119cb 100644
--- a/botocore/client.py
+++ b/botocore/client.py
@@ -10,7 +10,6 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-import re
 import copy
 import logging
 
@@ -18,6 +17,7 @@ import botocore.serialize
 import botocore.validate
 from botocore import waiter, xform_name
 from botocore.awsrequest import prepare_request_dict
+from botocore.compat import OrderedDict
 from botocore.endpoint import EndpointCreator, DEFAULT_TIMEOUT
 from botocore.exceptions import ClientError, DataNotFoundError
 from botocore.exceptions import OperationNotPageableError
@@ -377,18 +377,30 @@ class BaseClient(object):
         return self.meta.service_model
 
     def _make_api_call(self, operation_name, api_params):
+        request_context = {}
         operation_model = self._service_model.operation_model(operation_name)
         request_dict = self._convert_to_request_dict(
-            api_params, operation_model)
-        http, parsed_response = self._endpoint.make_request(
-            operation_model, request_dict)
+            api_params, operation_model, context=request_context)
+
+        handler, event_response = self.meta.events.emit_until_response(
+            'before-call.{endpoint_prefix}.{operation_name}'.format(
+                endpoint_prefix=self._service_model.endpoint_prefix,
+                operation_name=operation_name),
+            model=operation_model, params=request_dict,
+            request_signer=self._request_signer, context=request_context)
+
+        if event_response is not None:
+            http, parsed_response = event_response
+        else:
+            http, parsed_response = self._endpoint.make_request(
+                operation_model, request_dict)
 
         self.meta.events.emit(
             'after-call.{endpoint_prefix}.{operation_name}'.format(
                 endpoint_prefix=self._service_model.endpoint_prefix,
                 operation_name=operation_name),
             http_response=http, parsed=parsed_response,
-            model=operation_model
+            model=operation_model, context=request_context
         )
 
         if http.status_code >= 300:
@@ -396,7 +408,8 @@ class BaseClient(object):
         else:
             return parsed_response
 
-    def _convert_to_request_dict(self, api_params, operation_model):
+    def _convert_to_request_dict(self, api_params, operation_model,
+                                 context=None):
         # Given the API params provided by the user and the operation_model
         # we can serialize the request to a request_dict.
         operation_name = operation_model.name
@@ -408,7 +421,7 @@ class BaseClient(object):
             'provide-client-params.{endpoint_prefix}.{operation_name}'.format(
                 endpoint_prefix=self._service_model.endpoint_prefix,
                 operation_name=operation_name),
-            params=api_params, model=operation_model)
+            params=api_params, model=operation_model, context=context)
         api_params = first_non_none_response(responses, default=api_params)
 
         event_name = (
@@ -417,19 +430,12 @@ class BaseClient(object):
             event_name.format(
                 endpoint_prefix=self._service_model.endpoint_prefix,
                 operation_name=operation_name),
-            params=api_params, model=operation_model)
+            params=api_params, model=operation_model, context=context)
 
         request_dict = self._serializer.serialize_to_request(
             api_params, operation_model)
         prepare_request_dict(request_dict, endpoint_url=self._endpoint.host,
                              user_agent=self._client_config.user_agent)
-        self.meta.events.emit(
-            'before-call.{endpoint_prefix}.{operation_name}'.format(
-                endpoint_prefix=self._service_model.endpoint_prefix,
-                operation_name=operation_name),
-            model=operation_model, params=request_dict,
-            request_signer=self._request_signer
-        )
         return request_dict
 
     def get_paginator(self, operation_name):
@@ -612,11 +618,13 @@ class Config(object):
 
     :type connect_timeout: int
     :param connect_timeout: The time in seconds till a timeout exception is
-        thrown when attempting to make a connection.
+        thrown when attempting to make a connection. The default is 60
+        seconds.
 
     :type read_timeout: int
     :param read_timeout: The time in seconds till a timeout exception is
-        thrown when attempting to read from a connection.
+        thrown when attempting to read from a connection. The default is
+        60 seconds.
 
     :type s3: dict
     :param s3: A dictionary of s3 specific configurations.
@@ -635,19 +643,63 @@ class Config(object):
                   * path -- Addressing style is always by path. Endpoints will
                             be addressed as such: s3.amazonaws.com/mybucket
     """
-    def __init__(self, region_name=None, signature_version=None,
-                 user_agent=None, user_agent_extra=None,
-                 connect_timeout=DEFAULT_TIMEOUT,
-                 read_timeout=DEFAULT_TIMEOUT,
-                 s3=None):
-        self.region_name = region_name
-        self.signature_version = signature_version
-        self.user_agent = user_agent
-        self.user_agent_extra = user_agent_extra
-        self.connect_timeout = connect_timeout
-        self.read_timeout = read_timeout
-        self._validate_s3_configuration(s3)
-        self.s3 = s3
+    OPTION_DEFAULTS = OrderedDict([
+        ('region_name', None),
+        ('signature_version', None),
+        ('user_agent', None),
+        ('user_agent_extra', None),
+        ('connect_timeout', DEFAULT_TIMEOUT),
+        ('read_timeout', DEFAULT_TIMEOUT),
+        ('s3', None)
+    ])
+
+    def __init__(self, *args, **kwargs):
+        self._user_provided_options = self._record_user_provided_options(
+            args, kwargs)
+
+        # Merge the user_provided options onto the default options
+        config_vars = copy.copy(self.OPTION_DEFAULTS)
+        config_vars.update(self._user_provided_options)
+
+        # Set the attributes based on the config_vars
+        for key, value in config_vars.items():
+            setattr(self, key, value)
+
+        # Validate the s3 options
+        self._validate_s3_configuration(self.s3)
+
+    def _record_user_provided_options(self, args, kwargs):
+        option_order = list(self.OPTION_DEFAULTS)
+        user_provided_options = {}
+
+        # Iterate through the kwargs passed through to the constructor and
+        # map valid keys to the dictionary
+        for key, value in kwargs.items():
+            if key in self.OPTION_DEFAULTS:
+                user_provided_options[key] = value
+            # The key must exist in the available options
+            else:
+                raise TypeError(
+                    'Got unexpected keyword argument \'%s\'' % key)
+
+        # The number of args should not be longer than the allowed
+        # options
+        if len(args) > len(option_order):
+            raise TypeError(
+                'Takes at most %s arguments (%s given)' % (
+                    len(option_order), len(args)))
+
+        # Iterate through the args passed through to the constructor and map
+        # them to appropriate keys.
+        for i, arg in enumerate(args):
+            # If it a kwarg was specified for the arg, then error out
+            if option_order[i] in user_provided_options:
+                raise TypeError(
+                    'Got multiple values for keyword argument \'%s\'' % (
+                        option_order[i]))
+            user_provided_options[option_order[i]] = arg
+
+        return user_provided_options
 
     def _validate_s3_configuration(self, s3):
         if s3 is not None:
@@ -655,3 +707,26 @@ class Config(object):
             if addressing_style not in ['virtual', 'auto', 'path', None]:
                 raise InvalidS3AddressingStyleError(
                     s3_addressing_style=addressing_style)
+
+    def merge(self, other_config):
+        """Merges the config object with another config object
+
+        This will merge in all non-default values from the provided config
+        and return a new config object
+
+        :type other_config: botocore.client.Config
+        :param other config: Another config object to merge with. The values
+            in the provided config object will take precedence in the merging
+
+        :rtype: botocore.client.Config
+        :returns: A config object built from the merged values of both
+            config objects.
+        """
+        # Make a copy of the current attributes in the config object.
+        config_options = copy.copy(self._user_provided_options)
+
+        # Merge in the user provided options from the other config
+        config_options.update(other_config._user_provided_options)
+
+        # Return a new config object with the merged properties.
+        return Config(**config_options)
diff --git a/botocore/compat.py b/botocore/compat.py
index 343fc05..0bc4f2d 100644
--- a/botocore/compat.py
+++ b/botocore/compat.py
@@ -29,6 +29,7 @@ if six.PY3:
     from urllib.parse import urlencode
     from urllib.parse import unquote
     from urllib.parse import unquote_plus
+    from urllib.parse import urlparse
     from urllib.parse import urlsplit
     from urllib.parse import urlunsplit
     from urllib.parse import urljoin
@@ -69,6 +70,7 @@ else:
     from urllib import urlencode
     from urllib import unquote
     from urllib import unquote_plus
+    from urlparse import urlparse
     from urlparse import urlsplit
     from urlparse import urlunsplit
     from urlparse import urljoin
diff --git a/botocore/credentials.py b/botocore/credentials.py
index f4195d2..22163c0 100644
--- a/botocore/credentials.py
+++ b/botocore/credentials.py
@@ -11,14 +11,12 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
-
+import time
 import datetime
-import functools
 import logging
 import os
+import getpass
 
-from botocore.compat import six
-from six.moves import configparser
 from dateutil.parser import parse
 from dateutil.tz import tzlocal
 
@@ -28,6 +26,8 @@ from botocore.compat import total_seconds
 from botocore.exceptions import UnknownCredentialError
 from botocore.exceptions import PartialCredentialsError
 from botocore.exceptions import ConfigNotFound
+from botocore.exceptions import InvalidConfigError
+from botocore.exceptions import RefreshWithMFAUnsupportedError
 from botocore.utils import InstanceMetadataFetcher, parse_key_val_file
 
 
@@ -51,6 +51,12 @@ def create_credential_resolver(session):
     env_provider = EnvProvider()
     providers = [
         env_provider,
+        AssumeRoleProvider(
+            load_config=lambda: session.full_config,
+            client_creator=session.create_client,
+            cache={},
+            profile_name=profile_name,
+        ),
         SharedCredentialProvider(
             creds_filename=credential_file,
             profile_name=profile_name
@@ -103,6 +109,42 @@ def _local_now():
     return datetime.datetime.now(tzlocal())
 
 
+def _parse_if_needed(value):
+    if isinstance(value, datetime.datetime):
+        return value
+    return parse(value)
+
+
+def _serialize_if_needed(value):
+    if isinstance(value, datetime.datetime):
+        return value.strftime('%Y-%m-%dT%H:%M:%SZ')
+    return value
+
+
+def create_assume_role_refresher(client, params):
+    def refresh():
+        response = client.assume_role(**params)
+        credentials = response['Credentials']
+        # We need to normalize the credential names to
+        # the values expected by the refresh creds.
+        return {
+            'access_key': credentials['AccessKeyId'],
+            'secret_key': credentials['SecretAccessKey'],
+            'token': credentials['SessionToken'],
+            'expiry_time': _serialize_if_needed(credentials['Expiration']),
+        }
+    return refresh
+
+
+def create_mfa_serial_refresher():
+    def _refresher():
+        # We can explore an option in the future to support
+        # reprompting for MFA, but for now we just error out
+        # when the temp creds expire.
+        raise RefreshWithMFAUnsupportedError()
+    return _refresher
+
+
 class Credentials(object):
     """
     Holds the credentials needed to authenticate requests.
@@ -544,6 +586,211 @@ class BotoProvider(CredentialProvider):
                                        method=self.METHOD)
 
 
+class AssumeRoleProvider(CredentialProvider):
+
+    METHOD = 'assume-role'
+    ROLE_CONFIG_VAR = 'role_arn'
+    # Credentials are considered expired (and will be refreshed) once the total
+    # remaining time left until the credentials expires is less than the
+    # EXPIRY_WINDOW.
+    EXPIRY_WINDOW_SECONDS = 60 * 15
+
+    def __init__(self, load_config, client_creator, cache, profile_name,
+                 prompter=getpass.getpass):
+        """
+
+        :type load_config: callable
+        :param load_config: A function that accepts no arguments, and
+            when called, will return the full configuration dictionary
+            for the session (``session.full_config``).
+
+        :type client_creator: callable
+        :param client_creator: A factory function that will create
+            a client when called.  Has the same interface as
+            ``botocore.session.Session.create_client``.
+
+        :type cache: JSONFileCache
+        :param cache: An object that supports ``__getitem__``,
+            ``__setitem__``, and ``__contains__``.  An example
+            of this is the ``JSONFileCache`` class.
+
+        :type profile_name: str
+        :param profile_name: The name of the profile.
+
+        :type prompter: callable
+        :param prompter: A callable that returns input provided
+            by the user (i.e raw_input, getpass.getpass, etc.).
+
+        """
+        #: The cache used to first check for assumed credentials.
+        #: This is checked before making the AssumeRole API
+        #: calls and can be useful if you have short lived
+        #: scripts and you'd like to avoid calling AssumeRole
+        #: until the credentials are expired.
+        self.cache = cache
+        self._load_config = load_config
+        # client_creator is a callable that creates function.
+        # It's basically session.create_client
+        self._client_creator = client_creator
+        self._profile_name = profile_name
+        self._prompter = prompter
+        # The _loaded_config attribute will be populated from the
+        # load_config() function once the configuration is actually
+        # loaded.  The reason we go through all this instead of just
+        # requiring that the loaded_config be passed to us is to that
+        # we can defer configuration loaded until we actually try
+        # to load credentials (as opposed to when the object is
+        # instantiated).
+        self._loaded_config = {}
+
+    def load(self):
+        self._loaded_config = self._load_config()
+        if self._has_assume_role_config_vars():
+            return self._load_creds_via_assume_role()
+
+    def _has_assume_role_config_vars(self):
+        profiles = self._loaded_config.get('profiles', {})
+        return self.ROLE_CONFIG_VAR in profiles.get(self._profile_name, {})
+
+    def _load_creds_via_assume_role(self):
+        # We can get creds in one of two ways:
+        # * It can either be cached on disk from an pre-existing session
+        # * Cache doesn't have the creds (or is expired) so we need to make
+        #   an assume role call to get temporary creds, which we then cache
+        #   for subsequent requests.
+        creds = self._load_creds_from_cache()
+        if creds is not None:
+            logger.debug("Credentials for role retrieved from cache.")
+            return creds
+        else:
+            # We get the Credential used by botocore as well
+            # as the original parsed response from the server.
+            creds, response = self._retrieve_temp_credentials()
+            cache_key = self._create_cache_key()
+            self._write_cached_credentials(response, cache_key)
+            return creds
+
+    def _load_creds_from_cache(self):
+        cache_key = self._create_cache_key()
+        try:
+            from_cache = self.cache[cache_key]
+            if self._is_expired(from_cache):
+                # Don't need to delete the cache entry,
+                # when we refresh via AssumeRole, we'll
+                # update the cache with the new entry.
+                logger.debug("Credentials were found in cache, but they are expired.")
+                return None
+            else:
+                return self._create_creds_from_response(from_cache)
+        except KeyError:
+            return None
+
+    def _is_expired(self, credentials):
+        end_time = parse(credentials['Credentials']['Expiration'])
+        now = datetime.datetime.now(tzlocal())
+        seconds = total_seconds(end_time - now)
+        return seconds < self.EXPIRY_WINDOW_SECONDS
+
+    def _create_cache_key(self):
+        role_config = self._get_role_config_values()
+        # On windows, ':' is not allowed in filenames, so we'll
+        # replace them with '_' instead.
+        role_arn = role_config['role_arn'].replace(':', '_')
+        role_session_name=role_config.get('role_session_name')
+        if role_session_name:
+            cache_key = '%s--%s--%s' % (self._profile_name, role_arn, role_session_name)
+        else:
+            cache_key = '%s--%s' % (self._profile_name, role_arn)
+
+        return cache_key.replace('/', '-')
+
+    def _write_cached_credentials(self, creds, cache_key):
+        self.cache[cache_key] = creds
+
+    def _get_role_config_values(self):
+        # This returns the role related configuration.
+        profiles = self._loaded_config.get('profiles', {})
+        try:
+            source_profile = profiles[self._profile_name]['source_profile']
+            role_arn = profiles[self._profile_name]['role_arn']
+            mfa_serial = profiles[self._profile_name].get('mfa_serial')
+        except KeyError as e:
+            raise PartialCredentialsError(provider=self.METHOD,
+                                          cred_var=str(e))
+        external_id = profiles[self._profile_name].get('external_id')
+        role_session_name = profiles[self._profile_name].get('role_session_name')
+        if source_profile not in profiles:
+            raise InvalidConfigError(
+                error_msg=(
+                    'The source_profile "%s" referenced in '
+                    'the profile "%s" does not exist.' % (
+                        source_profile, self._profile_name)))
+        source_cred_values = profiles[source_profile]
+        return {
+            'role_arn': role_arn,
+            'external_id': external_id,
+            'source_profile': source_profile,
+            'mfa_serial': mfa_serial,
+            'source_cred_values': source_cred_values,
+            'role_session_name': role_session_name
+        }
+
+    def _create_creds_from_response(self, response):
+        config = self._get_role_config_values()
+        if config.get('mfa_serial') is not None:
+            # MFA would require getting a new TokenCode which would require
+            # prompting the user for a new token, so we use a different
+            # refresh_func.
+            refresh_func = create_mfa_serial_refresher()
+        else:
+            refresh_func = create_assume_role_refresher(
+                self._create_client_from_config(config),
+                self._assume_role_base_kwargs(config))
+        return RefreshableCredentials(
+            access_key=response['Credentials']['AccessKeyId'],
+            secret_key=response['Credentials']['SecretAccessKey'],
+            token=response['Credentials']['SessionToken'],
+            method=self.METHOD,
+            expiry_time=_parse_if_needed(
+                response['Credentials']['Expiration']),
+            refresh_using=refresh_func)
+
+    def _create_client_from_config(self, config):
+        source_cred_values = config['source_cred_values']
+        client = self._client_creator(
+            'sts', aws_access_key_id=source_cred_values['aws_access_key_id'],
+            aws_secret_access_key=source_cred_values['aws_secret_access_key'],
+            aws_session_token=source_cred_values.get('aws_session_token'),
+        )
+        return client
+
+    def _retrieve_temp_credentials(self):
+        logger.debug("Retrieving credentials via AssumeRole.")
+        config = self._get_role_config_values()
+        client = self._create_client_from_config(config)
+
+        assume_role_kwargs = self._assume_role_base_kwargs(config)
+        if assume_role_kwargs.get('RoleSessionName') is None:
+            role_session_name = 'AWS-CLI-session-%s' % (int(time.time()))
+            assume_role_kwargs['RoleSessionName'] = role_session_name
+
+        response = client.assume_role(**assume_role_kwargs)
+        creds = self._create_creds_from_response(response)
+        return creds, response
+
+    def _assume_role_base_kwargs(self, config):
+        assume_role_kwargs = {'RoleArn': config['role_arn']}
+        if config['external_id'] is not None:
+            assume_role_kwargs['ExternalId'] = config['external_id']
+        if config['mfa_serial'] is not None:
+            token_code = self._prompter("Enter MFA code: ")
+            assume_role_kwargs['SerialNumber'] = config['mfa_serial']
+            assume_role_kwargs['TokenCode'] = token_code
+        if config['role_session_name'] is not None:
+            assume_role_kwargs['RoleSessionName'] = config['role_session_name']
+        return assume_role_kwargs
+
+
 class CredentialResolver(object):
 
     def __init__(self, providers):
@@ -588,11 +835,7 @@ class CredentialResolver(object):
             you'd like to add to the chain.
         :type cred_instance: A subclass of ``Credentials``
         """
-        try:
-            offset = [p.METHOD for p in self.providers].index(name)
-        except ValueError:
-            raise UnknownCredentialError(name=name)
-
+        offset = self._get_provider_offset(name)
         self.providers.insert(offset + 1, credential_provider)
 
     def remove(self, name):
@@ -610,6 +853,24 @@ class CredentialResolver(object):
         offset = available_methods.index(name)
         self.providers.pop(offset)
 
+    def get_provider(self, name):
+        """Return a credential provider by name.
+
+        :type name: str
+        :param name: The name of the provider.
+
+        :raises UnknownCredentialError: Raised if no
+            credential provider by the provided name
+            is found.
+        """
+        return self.providers[self._get_provider_offset(name)]
+
+    def _get_provider_offset(self, name):
+        try:
+            return [p.METHOD for p in self.providers].index(name)
+        except ValueError:
+            raise UnknownCredentialError(name=name)
+
     def load_credentials(self):
         """
         Goes through the credentials chain, returning the first ``Credentials``
diff --git a/botocore/data/_endpoints.json b/botocore/data/_endpoints.json
index 02ffffb..6d8eb69 100644
--- a/botocore/data/_endpoints.json
+++ b/botocore/data/_endpoints.json
@@ -121,7 +121,12 @@
       "uri":"https://route53.amazonaws.com",
       "constraints": [
         ["region", "notStartsWith", "cn-"]
-      ]
+      ],
+      "properties": {
+        "credentialScope": {
+            "region": "us-east-1"
+        }
+      }
     }
   ],
   "waf":[
diff --git a/botocore/data/_retry.json b/botocore/data/_retry.json
index 90eae9b..a032554 100644
--- a/botocore/data/_retry.json
+++ b/botocore/data/_retry.json
@@ -16,6 +16,13 @@
         }
       }
     },
+    "too_many_requests": {
+      "applies_when": {
+        "response": {
+          "http_status_code": 429
+        }
+      }
+    },
     "general_socket_errors": {
       "applies_when": {
         "socket_errors": ["GENERAL_CONNECTION_ERROR"]
@@ -57,7 +64,8 @@
           "service_unavailable": {"$ref": "service_unavailable"},
           "limit_exceeded": {"$ref": "limit_exceeded"},
           "throttling_exception": {"$ref": "throttling_exception"},
-          "throttling": {"$ref": "throttling"}
+          "throttling": {"$ref": "throttling"},
+          "too_many_requests": {"$ref": "too_many_requests"}
       }
     },
     "dynamodb": {
diff --git a/botocore/data/acm/2015-12-08/service-2.json b/botocore/data/acm/2015-12-08/service-2.json
new file mode 100644
index 0000000..7a49bb8
--- /dev/null
+++ b/botocore/data/acm/2015-12-08/service-2.json
@@ -0,0 +1,585 @@
+{
+  "version":"2.0",
+  "metadata":{
+    "apiVersion":"2015-12-08",
+    "endpointPrefix":"acm",
+    "jsonVersion":"1.1",
+    "serviceAbbreviation":"ACM",
+    "serviceFullName":"AWS Certificate Manager",
+    "signatureVersion":"v4",
+    "targetPrefix":"CertificateManager",
+    "protocol":"json"
+  },
+  "documentation":"<fullname>AWS Certificate Manager</fullname> <p>Welcome to the AWS Certificate Manager (ACM) CLI Command Reference. This guide provides descriptions, syntax, and usage examples for each ACM CLI command. You can use AWS Certificate Manager to request ACM Certificates for your AWS-based websites and applications. For general information about using ACM and for more information about using the console, see the <a href=\"url-acm-ug;acm-overview.html\">AWS Certificate Manag [...]
+  "operations":{
+    "DeleteCertificate":{
+      "name":"DeleteCertificate",
+      "http":{
+        "method":"POST",
+        "requestUri":"/"
+      },
+      "input":{"shape":"DeleteCertificateRequest"},
+      "errors":[
+        {
+          "shape":"ResourceNotFoundException",
+          "exception":true,
+          "documentation":"<p> The specified certificate cannot be found in the caller's account, or the caller's account cannot be found. </p>"
+        },
+        {
+          "shape":"ResourceInUseException",
+          "exception":true,
+          "documentation":"<p> The certificate is in use by another AWS service in the caller's account. Remove the association and try again. </p>"
+        },
+        {
+          "shape":"InvalidArnException",
+          "exception":true,
+          "documentation":"<p> The requested Amazon Resource Name (ARN) does not refer to an existing resource. </p>"
+        }
+      ],
+      "documentation":"<p> Deletes an ACM Certificate and its associated private key. If this action succeeds, the certificate no longer appears in the list of ACM Certificates that can be displayed by calling the <a>ListCertificates</a> action or be retrieved by calling the <a>GetCertificate</a> action. The certificate will not be available for use by other AWS services.</p> <note>You cannot delete an ACM Certificate that is being used by another AWS service. To delete a certificate tha [...]
+    },
+    "DescribeCertificate":{
+      "name":"DescribeCertificate",
+      "http":{
+        "method":"POST",
+        "requestUri":"/"
+      },
+      "input":{"shape":"DescribeCertificateRequest"},
+      "output":{"shape":"DescribeCertificateResponse"},
+      "errors":[
+        {
+          "shape":"ResourceNotFoundException",
+          "exception":true,
+          "documentation":"<p> The specified certificate cannot be found in the caller's account, or the caller's account cannot be found. </p>"
+        },
+        {
+          "shape":"InvalidArnException",
+          "exception":true,
+          "documentation":"<p> The requested Amazon Resource Name (ARN) does not refer to an existing resource. </p>"
+        }
+      ],
+      "documentation":"<p> Returns a list of the fields contained in the specified ACM Certificate. For example, this action returns the certificate status, a flag that indicates whether the certificate is associated with any other AWS service, and the date at which the certificate request was created. The ACM Certificate is specified on input by its Amazon Resource Name (ARN). </p>"
+    },
+    "GetCertificate":{
+      "name":"GetCertificate",
+      "http":{
+        "method":"POST",
+        "requestUri":"/"
+      },
+      "input":{"shape":"GetCertificateRequest"},
+      "output":{"shape":"GetCertificateResponse"},
+      "errors":[
+        {
+          "shape":"ResourceNotFoundException",
+          "exception":true,
+          "documentation":"<p> The specified certificate cannot be found in the caller's account, or the caller's account cannot be found. </p>"
+        },
+        {
+          "shape":"RequestInProgressException",
+          "exception":true,
+          "documentation":"<p> The certificate request is in process and the certificate in your account has not yet been issued. </p>"
+        },
+        {
+          "shape":"InvalidArnException",
... 51218 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-botocore.git



More information about the Python-modules-commits mailing list