Hello community,

here is the log from the commit of package python-boto3 for openSUSE:Factory 
checked in at 2016-04-14 13:05:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-boto3 (Old)
 and      /work/SRC/openSUSE:Factory/.python-boto3.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-boto3"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-boto3/python-boto3.changes        
2016-02-11 12:37:52.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python-boto3.new/python-boto3.changes   
2016-04-14 13:05:48.000000000 +0200
@@ -1,0 +2,21 @@
+Fri Apr  8 18:44:56 UTC 2016 - [email protected]
+
+- Fix version requirement for botocore
+
+-------------------------------------------------------------------
+Fri Apr  8 17:47:00 UTC 2016 - [email protected]
+
+- Update to version 1.3.0 (bsc#974705)
+  + feature:Session: Add get_available_partitions and get_available_regions
+     methods to determine partitions and a service's available regions.
+  + feature:EC2: Update resource model to include Route resources. (issue 532)
+- From 1.2.6 - (2016-03-01)
+  + bugfix:Resources: Properly alias identifiers which are also in the shape.
+- From 1.2.5 - (2016-02-25)
+  + bugfix:S3: Forward extra_args when using multipart downloads. (issue 503)
+- From 1.2.4 - (2016-02-18)
+  + feature:EC2: Add delete_tags() action to Instance resource. (issue 459)
+  + feature:Session: Add region_name property on session. (issue 414)
+  + bugfix:S3: Fix issue with hanging downloads. (issue 471)
+
+-------------------------------------------------------------------

Old:
----
  boto3-1.2.3.tar.gz

New:
----
  boto3-1.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-boto3.spec ++++++
--- /var/tmp/diff_new_pack.qF9c00/_old  2016-04-14 13:05:49.000000000 +0200
+++ /var/tmp/diff_new_pack.qF9c00/_new  2016-04-14 13:05:49.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-boto3
-Version:        1.2.3
+Version:        1.3.0
 Release:        0
 Url:            https://github.com/boto/boto3
 Summary:        Amazon Web Services Library
@@ -28,8 +28,8 @@
 BuildRequires:  python-devel
 BuildRequires:  python-futures
 BuildRequires:  python-setuptools
-Requires:       python-botocore < 1.4
-Requires:       python-botocore >= 1.3.0
+Requires:       python-botocore < 1.5
+Requires:       python-botocore >= 1.4.1
 Requires:       python-futures
 Requires:       python-jmespath < 1.0.0
 Requires:       python-jmespath >= 0.7.1

++++++ boto3-1.2.3.tar.gz -> boto3-1.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/LICENSE new/boto3-1.3.0/LICENSE
--- old/boto3-1.2.3/LICENSE     2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/LICENSE     2016-03-16 01:14:39.000000000 +0100
@@ -1,4 +1,4 @@
-Copyright 2013-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+Copyright 2013-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License"). You
 may not use this file except in compliance with the License. A copy of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/PKG-INFO new/boto3-1.3.0/PKG-INFO
--- old/boto3-1.2.3/PKG-INFO    2015-12-17 23:21:36.000000000 +0100
+++ new/boto3-1.3.0/PKG-INFO    2016-03-16 01:14:40.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: boto3
-Version: 1.2.3
+Version: 1.3.0
 Summary: The AWS SDK for Python
 Home-page: https://github.com/boto/boto3
 Author: Amazon Web Services
@@ -130,3 +130,4 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/__init__.py 
new/boto3-1.3.0/boto3/__init__.py
--- old/boto3-1.2.3/boto3/__init__.py   2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/__init__.py   2016-03-16 01:14:40.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 __author__ = 'Amazon Web Services'
-__version__ = '1.2.3'
+__version__ = '1.3.0'
 
 
 # The default Boto3 session; autoloaded when needed.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/compat.py 
new/boto3-1.3.0/boto3/compat.py
--- old/boto3-1.2.3/boto3/compat.py     2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/compat.py     2016-03-16 01:14:39.000000000 +0100
@@ -13,6 +13,18 @@
 import sys
 import os
 import errno
+import socket
+
+from botocore.vendored import six
+
+if six.PY3:
+    # In python3, socket.error is OSError, which is too general
+    # for what we want (i.e FileNotFoundError is a subclass of OSError).
+    # In py3 all the socket related errors are in a newly created
+    # ConnectionError
+    SOCKET_ERROR = ConnectionError
+else:
+    SOCKET_ERROR = socket.error
 
 
 if sys.platform.startswith('win'):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/boto3-1.2.3/boto3/data/dynamodb/2012-08-10/resources-1.json 
new/boto3-1.3.0/boto3/data/dynamodb/2012-08-10/resources-1.json
--- old/boto3-1.2.3/boto3/data/dynamodb/2012-08-10/resources-1.json     
2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/data/dynamodb/2012-08-10/resources-1.json     
2016-03-16 01:14:40.000000000 +0100
@@ -130,6 +130,20 @@
             ]
           }
         }
+      },
+      "waiters":{
+        "Exists": {
+          "waiterName": "TableExists",
+          "params": [
+            { "target": "TableName", "source": "identifier", "name": "Name" }
+          ]
+        },
+        "NotExists": {
+          "waiterName": "TableNotExists",
+          "params": [
+            { "target": "TableName", "source": "identifier", "name": "Name" }
+          ]
+        }
       }
     }
   }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/boto3-1.2.3/boto3/data/ec2/2015-10-01/resources-1.json 
new/boto3-1.3.0/boto3/data/ec2/2015-10-01/resources-1.json
--- old/boto3-1.2.3/boto3/data/ec2/2015-10-01/resources-1.json  2015-12-17 
23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/data/ec2/2015-10-01/resources-1.json  2016-03-16 
01:14:40.000000000 +0100
@@ -491,7 +491,7 @@
         "request": {
           "operation": "DescribeAddresses",
           "params": [
-            { "target": "PublicIp", "source": "identifier", "name": "PublicIp" 
}
+            { "target": "PublicIps[]", "source": "identifier", "name": 
"PublicIp" }
           ]
         },
         "path": "Addresses[0]"
@@ -1356,6 +1356,16 @@
         }
       ],
       "shape": "InstanceNetworkInterfaceAssociation",
+      "load": {
+        "request": {
+          "operation": "DescribeNetworkInterfaces",
+          "params": [
+            { "target": "Filters[0].Name", "source": "string", "value": 
"association.association-id" },
+            { "target": "Filters[0].Values[0]", "source": "identifier", 
"name": "Id" }
+          ]
+        },
+        "path": "NetworkInterfaces[0].Association"
+      },
       "actions": {
         "Delete": {
           "request": {
@@ -1423,6 +1433,46 @@
         }
       }
     },
+    "Route": {
+      "identifiers": [
+        { "name": "RouteTableId" },
+        {
+          "name": "DestinationCidrBlock",
+          "memberName": "DestinationCidrBlock"
+        }
+      ],
+      "shape": "Route",
+      "actions": {
+        "Delete": {
+          "request": {
+            "operation": "DeleteRoute",
+            "params": [
+              { "target": "RouteTableId", "source": "identifier", "name": 
"RouteTableId" },
+              { "target": "DestinationCidrBlock", "source": "identifier", 
"name": "DestinationCidrBlock" }
+            ]
+          }
+        },
+        "Replace": {
+          "request": {
+            "operation": "ReplaceRoute",
+            "params": [
+              { "target": "RouteTableId", "source": "identifier", "name": 
"RouteTableId" },
+              { "target": "DestinationCidrBlock", "source": "identifier", 
"name": "DestinationCidrBlock" }
+            ]
+          }
+        }
+      },
+      "has": {
+        "RouteTable": {
+          "resource": {
+            "type": "RouteTable",
+            "identifiers": [
+              { "target": "Id", "source": "identifier", "name": "RouteTableId" 
}
+            ]
+          }
+        }
+      }
+    },
     "RouteTable": {
       "identifiers": [
         {
@@ -1461,6 +1511,13 @@
             "params": [
               { "target": "RouteTableId", "source": "identifier", "name": "Id" 
}
             ]
+          },
+          "resource": {
+            "type": "Route",
+            "identifiers": [
+              { "target": "RouteTableId", "source": "identifier", "name": "Id" 
},
+              { "target": "DestinationCidrBlock", "source": 
"requestParameter", "path": "DestinationCidrBlock" }
+            ]
           }
         },
         "CreateTags": {
@@ -1489,6 +1546,16 @@
         }
       },
       "has": {
+        "Routes": {
+          "resource": {
+            "type": "Route",
+            "identifiers": [
+              { "target": "RouteTableId", "source": "identifier", "name": "Id" 
},
+              { "target": "DestinationCidrBlock", "source": "data", "path": 
"Routes[].DestinationCidrBlock" }
+            ],
+            "path": "Routes[]"
+          }
+        },
         "Vpc": {
           "resource": {
             "type": "Vpc",
@@ -2423,6 +2490,15 @@
           }
         }
       },
+      "waiters": {
+        "Exists": {
+          "waiterName": "VpcPeeringConnectionExists",
+          "params": [
+            { "target": "VpcPeeringConnectionIds[]", "source": "identifier", 
"name": "Id" }
+          ],
+          "path": "VpcPeeringConnections[0]"
+        }
+      },
       "has": {
         "AccepterVpc": {
           "resource": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/docs/action.py 
new/boto3-1.3.0/boto3/docs/action.py
--- old/boto3-1.2.3/boto3/docs/action.py        2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/docs/action.py        2016-03-16 01:14:39.000000000 
+0100
@@ -40,6 +40,7 @@
                 'automatically handle the passing in of arguments set '
                 'from identifiers and some attributes.'),
             intro_link='actions_intro')
+
         for action_name in sorted(resource_actions):
             action_section = section.add_new_section(action_name)
             if action_name in ['load', 'reload'] and self._resource_model.load:
@@ -61,7 +62,7 @@
                 )
             else:
                 document_custom_method(
-                    section, action_name, resource_actions[action_name])
+                    action_section, action_name, resource_actions[action_name])
 
 
 def document_action(section, resource_name, event_emitter, action_model,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/docs/attr.py 
new/boto3-1.3.0/boto3/docs/attr.py
--- old/boto3-1.2.3/boto3/docs/attr.py  2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/docs/attr.py  2016-03-16 01:14:40.000000000 +0100
@@ -11,16 +11,28 @@
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
 from botocore.docs.utils import py_type_name
+from botocore.docs.params import ResponseParamsDocumenter
 
 from boto3.docs.utils import get_identifier_description
 
 
-def document_attribute(section, attr_name, attr_model, include_signature=True):
+class ResourceShapeDocumenter(ResponseParamsDocumenter):
+    EVENT_NAME = 'resource-shape'
+
+
+def document_attribute(section, service_name, resource_name, attr_name,
+                       event_emitter, attr_model, include_signature=True):
     if include_signature:
         section.style.start_sphinx_py_attr(attr_name)
-    attr_type = '*(%s)* ' % py_type_name(attr_model.type_name)
-    section.write(attr_type)
-    section.include_doc_string(attr_model.documentation)
+    # Note that an attribute may have one, may have many, or may have no
+    # operations that back the resource's shape. So we just set the
+    # operation_name to the resource name if we ever to hook in and modify
+    # a particular attribute.
+    ResourceShapeDocumenter(
+        service_name=service_name, operation_name=resource_name,
+        event_emitter=event_emitter).document_params(
+            section=section,
+            shape=attr_model)
 
 
 def document_identifier(section, resource_name, identifier_model,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/docs/resource.py 
new/boto3-1.3.0/boto3/docs/resource.py
--- old/boto3-1.2.3/boto3/docs/resource.py      2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/docs/resource.py      2016-03-16 01:14:40.000000000 
+0100
@@ -169,7 +169,10 @@
             attribute_list.append(attr_name)
             document_attribute(
                 section=attribute_section,
+                service_name=self._service_name,
+                resource_name=self._resource_name,
                 attr_name=attr_name,
+                event_emitter=self._resource.meta.client.meta.events,
                 attr_model=attr_shape
             )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/docs/service.py 
new/boto3-1.3.0/boto3/docs/service.py
--- old/boto3-1.2.3/boto3/docs/service.py       2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/docs/service.py       2016-03-16 01:14:39.000000000 
+0100
@@ -14,8 +14,7 @@
 
 import boto3
 from botocore.exceptions import DataNotFoundError
-from botocore.docs.paginator import PaginatorDocumenter
-from botocore.docs.waiter import WaiterDocumenter
+from botocore.docs.service import ServiceDocumenter as BaseServiceDocumenter
 from botocore.docs.bcdoc.restdoc import DocumentStructure
 
 from boto3.utils import ServiceContext
@@ -24,20 +23,20 @@
 from boto3.docs.resource import ServiceResourceDocumenter
 
 
-class ServiceDocumenter(object):
+class ServiceDocumenter(BaseServiceDocumenter):
     # The path used to find examples
     EXAMPLE_PATH = os.path.join(os.path.dirname(boto3.__file__), 'examples')
 
     def __init__(self, service_name, session):
         self._service_name = service_name
-        self._session = session
+        self._boto3_session = session
         # I know that this is an internal attribute, but the botocore session
         # is needed to load the paginator and waiter models.
-        self._botocore_session = session._session
-        self._client = self._session.client(service_name)
+        self._session = session._session
+        self._client = self._boto3_session.client(service_name)
         self._service_resource = None
-        if self._service_name in self._session.get_available_resources():
-            self._service_resource = self._session.resource(service_name)
+        if self._service_name in self._boto3_session.get_available_resources():
+            self._service_resource = self._boto3_session.resource(service_name)
         self.sections = [
             'title',
             'table-of-contents',
@@ -57,12 +56,12 @@
         doc_structure = DocumentStructure(
             self._service_name, section_names=self.sections,
             target='html')
-        self._document_title(doc_structure.get_section('title'))
-        self._document_table_of_contents(
-            doc_structure.get_section('table-of-contents'))
-        self._document_client(doc_structure.get_section('client'))
-        self._document_paginators(doc_structure.get_section('paginators'))
-        self._document_waiters(doc_structure.get_section('waiters'))
+        self.title(doc_structure.get_section('title'))
+        self.table_of_contents(doc_structure.get_section('table-of-contents'))
+
+        self.client_api(doc_structure.get_section('client'))
+        self.paginator_api(doc_structure.get_section('paginators'))
+        self.waiter_api(doc_structure.get_section('waiters'))
         if self._service_resource:
             self._document_service_resource(
                 doc_structure.get_section('service-resource'))
@@ -70,63 +69,47 @@
         self._document_examples(doc_structure.get_section('examples'))
         return doc_structure.flush_structure()
 
-    def _document_title(self, section):
-        section.style.h1(self._client.__class__.__name__)
-
-    def _document_table_of_contents(self, section):
-        section.style.table_of_contents(title='Table of Contents', depth=2)
-
-    def _document_client(self, section):
-        Boto3ClientDocumenter(self._client).document_client(section)
-
-    def _document_paginators(self, section):
+    def client_api(self, section):
+        examples = None
         try:
-            paginator_model = self._botocore_session.get_paginator_model(
-                self._service_name)
+            examples = self.get_examples(self._service_name)
         except DataNotFoundError:
-            return
-        paginator_documenter = PaginatorDocumenter(
-            self._client, paginator_model)
-        paginator_documenter.document_paginators(section)
-
-    def _document_waiters(self, section):
-        if self._client.waiter_names:
-            service_waiter_model = self._botocore_session.get_waiter_model(
-                self._service_name)
-            waiter_documenter = WaiterDocumenter(
-                self._client, service_waiter_model)
-            waiter_documenter.document_waiters(section)
+            pass
+
+        Boto3ClientDocumenter(self._client, examples).document_client(section)
 
     def _document_service_resource(self, section):
         ServiceResourceDocumenter(
-            self._service_resource, self._botocore_session).document_resource(
+            self._service_resource, self._session).document_resource(
                 section)
 
     def _document_resources(self, section):
         temp_identifier_value = 'foo'
-        loader = self._botocore_session.get_component('data_loader')
+        loader = self._session.get_component('data_loader')
         json_resource_model = loader.load_service_model(
             self._service_name, 'resources-1')
         service_model = self._service_resource.meta.client.meta.service_model
         for resource_name in json_resource_model['resources']:
             resource_model = json_resource_model['resources'][resource_name]
-            resource_cls = self._session.resource_factory.load_from_definition(
-                resource_name=resource_name,
-                single_resource_json_definition=resource_model,
-                service_context=ServiceContext(
-                    service_name=self._service_name,
-                    resource_json_definitions=json_resource_model['resources'],
-                    service_model=service_model,
-                    service_waiter_model=None
+            resource_cls = self._boto3_session.resource_factory.\
+                load_from_definition(
+                    resource_name=resource_name,
+                    single_resource_json_definition=resource_model,
+                    service_context=ServiceContext(
+                        service_name=self._service_name,
+                        resource_json_definitions=json_resource_model[
+                            'resources'],
+                        service_model=service_model,
+                        service_waiter_model=None
+                    )
                 )
-            )
             identifiers = resource_cls.meta.resource_model.identifiers
             args = []
             for _ in identifiers:
                 args.append(temp_identifier_value)
             resource = resource_cls(*args, client=self._client)
             ResourceDocumenter(
-                resource, self._botocore_session).document_resource(
+                resource, self._session).document_resource(
                     section.add_new_section(resource.meta.resource_model.name))
 
     def _get_example_file(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/ec2/deletetags.py 
new/boto3-1.3.0/boto3/ec2/deletetags.py
--- old/boto3-1.2.3/boto3/ec2/deletetags.py     1970-01-01 01:00:00.000000000 
+0100
+++ new/boto3-1.3.0/boto3/ec2/deletetags.py     2016-03-16 01:14:39.000000000 
+0100
@@ -0,0 +1,34 @@
+# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+from boto3.resources.action import CustomModeledAction
+
+
+def inject_delete_tags(event_emitter, **kwargs):
+    action_model = {
+        'request': {
+            'operation': 'DeleteTags',
+            'params': [{
+                'target': 'Resources[0]',
+                'source': 'identifier',
+                'name': 'Id'
+            }]
+        }
+    }
+    action = CustomModeledAction(
+        'delete_tags', action_model, delete_tags, event_emitter)
+    action.inject(**kwargs)
+
+
+def delete_tags(self, **kwargs):
+    kwargs['Resources'] = [self.id]
+    return self.meta.client.delete_tags(**kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/exceptions.py 
new/boto3-1.3.0/boto3/exceptions.py
--- old/boto3-1.2.3/boto3/exceptions.py 2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/exceptions.py 2016-03-16 01:14:40.000000000 +0100
@@ -11,29 +11,77 @@
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
 
-class ResourceLoadException(Exception):
+# All exceptions in this class should subclass from Boto3Error.
+import botocore.exceptions
+
+
+# All exceptions should subclass from Boto3Error in this module.
+class Boto3Error(Exception):
+    """Base class for all Boto3 errors."""
+
+
+class ResourceLoadException(Boto3Error):
     pass
 
 
-class NoVersionFound(Exception):
+# NOTE: This doesn't appear to be used anywhere.
+# It's probably safe to remove this.
+class NoVersionFound(Boto3Error):
     pass
 
 
-class RetriesExceededError(Exception):
+# We're subclassing from botocore.exceptions.DataNotFoundError
+# to keep backwards compatibility with anyone that was catching
+# this low level Botocore error before this exception was
+# introduced in boto3.
+# Same thing for ResourceNotExistsError below.
+class UnknownAPIVersionError(Boto3Error,
+                             botocore.exceptions.DataNotFoundError):
+    def __init__(self, service_name, bad_api_version,
+                 available_api_versions):
+        msg = (
+            "The '%s' resource does not an API version of: %s\n"
+            "Valid API versions are: %s"
+            % (service_name, bad_api_version, available_api_versions)
+        )
+        # Not using super because we don't want the DataNotFoundError
+        # to be called, it has a different __init__ signature.
+        Boto3Error.__init__(self, msg)
+
+
+class ResourceNotExistsError(Boto3Error,
+                             botocore.exceptions.DataNotFoundError):
+    """Raised when you attempt to create a resource that does not exist."""
+    def __init__(self, service_name, available_services, has_low_level_client):
+        msg = (
+            "The '%s' resource does not exist.\n"
+            "The available resources are:\n"
+            "   - %s\n" % (service_name, '\n   - '.join(available_services))
+        )
+        if has_low_level_client:
+            msg += (
+                "\nConsider using a boto3.client('%s') instead "
+                "of a resource for '%s'" % (service_name, service_name))
+        # Not using super because we don't want the DataNotFoundError
+        # to be called, it has a different __init__ signature.
+        Boto3Error.__init__(self, msg)
+
+
+class RetriesExceededError(Boto3Error):
     def __init__(self, last_exception, msg='Max Retries Exceeded'):
         super(RetriesExceededError, self).__init__(msg)
         self.last_exception = last_exception
 
 
-class S3TransferFailedError(Exception):
+class S3TransferFailedError(Boto3Error):
     pass
 
 
-class S3UploadFailedError(Exception):
+class S3UploadFailedError(Boto3Error):
     pass
 
 
-class DynamoDBOperationNotSupportedError(Exception):
+class DynamoDBOperationNotSupportedError(Boto3Error):
     """Raised for operantions that are not supported for an operand"""
     def __init__(self, operation, value):
         msg = (
@@ -46,7 +94,7 @@
 # FIXME: Backward compatibility
 DynanmoDBOperationNotSupportedError = DynamoDBOperationNotSupportedError
 
-class DynamoDBNeedsConditionError(Exception):
+class DynamoDBNeedsConditionError(Boto3Error):
     """Raised when input is not a condition"""
     def __init__(self, value):
         msg = (
@@ -56,5 +104,5 @@
         Exception.__init__(self, msg)
 
 
-class DynamoDBNeedsKeyConditionError(Exception):
+class DynamoDBNeedsKeyConditionError(Boto3Error):
     pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/resources/action.py 
new/boto3-1.3.0/boto3/resources/action.py
--- old/boto3-1.2.3/boto3/resources/action.py   2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/resources/action.py   2016-03-16 01:14:39.000000000 
+0100
@@ -17,6 +17,10 @@
 
 from .params import create_request_parameters
 from .response import RawHandler, ResourceHandler
+from .model import Action
+
+from boto3.docs.docstring import ActionDocstring
+from boto3.utils import inject_attribute
 
 
 logger = logging.getLogger(__name__)
@@ -197,3 +201,42 @@
         response = waiter.wait(**params)
 
         logger.debug('Response: %r', response)
+
+
+class CustomModeledAction(object):
+    """A custom, modeled action to inject into a resource."""
+    def __init__(self, action_name, action_model,
+                 function, event_emitter):
+        """
+        :type action_name: str
+        :param action_name: The name of the action to inject, e.g. 
'delete_tags'
+
+        :type action_model: dict
+        :param action_model: A JSON definition of the action, as if it were
+            part of the resource model.
+
+        :type function: function
+        :param function: The function to perform when the action is called.
+            The first argument should be 'self', which will be the resource
+            the function is to be called on.
+
+        :type event_emitter: :py:class:`botocore.hooks.BaseEventHooks`
+        :param event_emitter: The session event emitter.
+        """
+        self.name = action_name
+        self.model = action_model
+        self.function = function
+        self.emitter = event_emitter
+
+    def inject(self, class_attributes, service_context, event_name, **kwargs):
+        resource_name = event_name.rsplit(".")[-1]
+        action = Action(self.name, self.model, {})
+        self.function.__name__ = self.name
+        self.function.__doc__ = ActionDocstring(
+            resource_name=resource_name,
+            event_emitter=self.emitter,
+            action_model=action,
+            service_model=service_context.service_model,
+            include_signature=False
+        )
+        inject_attribute(class_attributes, self.name, self.function)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/resources/factory.py 
new/boto3-1.3.0/boto3/resources/factory.py
--- old/boto3-1.2.3/boto3/resources/factory.py  2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/resources/factory.py  2016-03-16 01:14:40.000000000 
+0100
@@ -105,7 +105,8 @@
 
         # Attributes that get auto-loaded
         self._load_attributes(
-            attrs=attrs, meta=meta, resource_model=resource_model,
+            attrs=attrs, meta=meta, resource_name=resource_name,
+            resource_model=resource_model,
             service_context=service_context)
 
         # Collections and their corresponding methods
@@ -133,9 +134,10 @@
 
         base_classes = [ServiceResource]
         if self._emitter is not None:
-            self._emitter.emit('creating-resource-class.%s' % cls_name,
-                               class_attributes=attrs,
-                               base_classes=base_classes)
+            self._emitter.emit(
+                'creating-resource-class.%s' % cls_name,
+                class_attributes=attrs, base_classes=base_classes,
+                service_context=service_context)
         return type(str(cls_name), tuple(base_classes), attrs)
 
     def _load_identifiers(self, attrs, meta, resource_model, resource_name):
@@ -167,21 +169,39 @@
                 action_model=action, resource_name=resource_name,
                 service_context=service_context)
 
-    def _load_attributes(self, attrs, meta, resource_model, service_context):
+    def _load_attributes(self, attrs, meta, resource_name, resource_model,
+                         service_context):
         """
         Load resource attributes based on the resource shape. The shape
         name is referenced in the resource JSON, but the shape itself
         is defined in the Botocore service JSON, hence the need for
         access to the ``service_model``.
         """
-        if resource_model.shape:
-            shape = service_context.service_model.shape_for(
-                resource_model.shape)
-
-            attributes = resource_model.get_attributes(shape)
-            for name, (orig_name, member) in attributes.items():
-                attrs[name] = self._create_autoload_property(
-                    name=orig_name, snake_cased=name, member_model=member)
+        if not resource_model.shape:
+            return
+
+        shape = service_context.service_model.shape_for(
+            resource_model.shape)
+
+        identifiers = dict((i.member_name, i)
+                           for i in resource_model.identifiers if 
i.member_name)
+        attributes = resource_model.get_attributes(shape)
+        for name, (orig_name, member) in attributes.items():
+            if name in identifiers:
+                prop = self._create_identifier_alias(
+                    resource_name=resource_name,
+                    identifier=identifiers[name],
+                    member_model=member,
+                    service_context=service_context
+                )
+            else:
+                prop = self._create_autoload_property(
+                    resource_name=resource_name,
+                    name=orig_name, snake_cased=name,
+                    member_model=member,
+                    service_context=service_context
+                )
+            attrs[name] = prop
 
     def _load_collections(self, attrs, resource_model, service_context):
         """
@@ -264,8 +284,28 @@
 
         return property(get_identifier)
 
-    def _create_autoload_property(factory_self, name, snake_cased,
-                                  member_model):
+    def _create_identifier_alias(factory_self, resource_name, identifier,
+                                 member_model, service_context):
+        """
+        Creates a read-only property that aliases an identifier.
+        """
+        def get_identifier(self):
+            return getattr(self, '_' + identifier.name, None)
+
+        get_identifier.__name__ = str(identifier.member_name)
+        get_identifier.__doc__ = docstring.AttributeDocstring(
+            service_name=service_context.service_name,
+            resource_name=resource_name,
+            attr_name=identifier.member_name,
+            event_emitter=factory_self._emitter,
+            attr_model=member_model,
+            include_signature=False
+        )
+
+        return property(get_identifier)
+
+    def _create_autoload_property(factory_self, resource_name, name,
+                                  snake_cased, member_model, service_context):
         """
         Creates a new property on the resource to lazy-load its value
         via the resource's ``load`` method (if it exists).
@@ -286,7 +326,10 @@
 
         property_loader.__name__ = str(snake_cased)
         property_loader.__doc__ = docstring.AttributeDocstring(
+            service_name=service_context.service_name,
+            resource_name=resource_name,
             attr_name=snake_cased,
+            event_emitter=factory_self._emitter,
             attr_model=member_model,
             include_signature=False
         )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/resources/model.py 
new/boto3-1.3.0/boto3/resources/model.py
--- old/boto3-1.2.3/boto3/resources/model.py    2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/resources/model.py    2016-03-16 01:14:39.000000000 
+0100
@@ -38,9 +38,10 @@
     :type name: string
     :param name: The name of the identifier
     """
-    def __init__(self, name):
+    def __init__(self, name, member_name=None):
         #: (``string``) The name of the identifier
         self.name = name
+        self.member_name = member_name
 
 
 class Action(object):
@@ -428,7 +429,10 @@
 
         for item in self._definition.get('identifiers', []):
             name = self._get_name('identifier', item['name'])
-            identifiers.append(Identifier(name))
+            member_name = item.get('memberName', None)
+            if member_name:
+                member_name = self._get_name('attribute', member_name)
+            identifiers.append(Identifier(name, member_name))
 
         return identifiers
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/s3/transfer.py 
new/boto3-1.3.0/boto3/s3/transfer.py
--- old/boto3-1.2.3/boto3/s3/transfer.py        2015-12-17 23:21:35.000000000 
+0100
+++ new/boto3-1.3.0/boto3/s3/transfer.py        2016-03-16 01:14:39.000000000 
+0100
@@ -148,6 +148,10 @@
 
 MB = 1024 * 1024
 SHUTDOWN_SENTINEL = object()
+S3_RETRYABLE_ERRORS = (
+    socket.timeout, boto3.compat.SOCKET_ERROR,
+    ReadTimeoutError, IncompleteReadError
+)
 
 
 def random_file_extension(num_digits=8):
@@ -463,7 +467,7 @@
             # 1 thread for the future that manages IO writes.
             download_parts_handler = functools.partial(
                 self._download_file_as_future,
-                bucket, key, filename, object_size, callback)
+                bucket, key, filename, object_size, extra_args, callback)
             parts_future = controller.submit(download_parts_handler)
 
             io_writes_handler = functools.partial(
@@ -479,13 +483,13 @@
             future.result()
 
     def _download_file_as_future(self, bucket, key, filename, object_size,
-                                 callback):
+                                 extra_args, callback):
         part_size = self._config.multipart_chunksize
         num_parts = int(math.ceil(object_size / float(part_size)))
         max_workers = self._config.max_concurrency
         download_partial = functools.partial(
             self._download_range, bucket, key, filename,
-            part_size, num_parts, callback)
+            part_size, num_parts, extra_args, callback)
         try:
             with self._executor_cls(max_workers=max_workers) as executor:
                 list(executor.map(download_partial, range(num_parts)))
@@ -502,7 +506,8 @@
         return range_param
 
     def _download_range(self, bucket, key, filename,
-                        part_size, num_parts, callback, part_index):
+                        part_size, num_parts,
+                        extra_args, callback, part_index):
         try:
             range_param = self._calculate_range_param(
                 part_size, part_index, num_parts)
@@ -513,7 +518,8 @@
                 try:
                     logger.debug("Making get_object call.")
                     response = self._client.get_object(
-                        Bucket=bucket, Key=key, Range=range_param)
+                        Bucket=bucket, Key=key, Range=range_param,
+                        **extra_args)
                     streaming_body = StreamReaderProgress(
                         response['Body'], callback)
                     buffer_size = 1024 * 16
@@ -523,8 +529,7 @@
                         self._ioqueue.put((current_index, chunk))
                         current_index += len(chunk)
                     return
-                except (socket.timeout, socket.error,
-                        ReadTimeoutError, IncompleteReadError) as e:
+                except S3_RETRYABLE_ERRORS as e:
                     logger.debug("Retrying exception caught (%s), "
                                  "retrying request, (attempt %s / %s)", e, i,
                                  max_attempts, exc_info=True)
@@ -535,6 +540,15 @@
             logger.debug("EXITING _download_range for part: %s", part_index)
 
     def _perform_io_writes(self, filename):
+        try:
+            self._loop_on_io_writes(filename)
+        except Exception as e:
+            logger.debug("Caught exception in IO thread: %s",
+                         e, exc_info=True)
+            self._ioqueue.trigger_shutdown()
+            raise
+
+    def _loop_on_io_writes(self, filename):
         with self._os.open(filename, 'wb') as f:
             while True:
                 task = self._ioqueue.get()
@@ -543,15 +557,9 @@
                                  "shutting down IO handler.")
                     return
                 else:
-                    try:
-                        offset, data = task
-                        f.seek(offset)
-                        f.write(data)
-                    except Exception as e:
-                        logger.debug("Caught exception in IO thread: %s",
-                                     e, exc_info=True)
-                        self._ioqueue.trigger_shutdown()
-                        raise
+                    offset, data = task
+                    f.seek(offset)
+                    f.write(data)
 
 
 class TransferConfig(object):
@@ -699,10 +707,7 @@
             try:
                 return self._do_get_object(bucket, key, filename,
                                            extra_args, callback)
-            except (socket.timeout, socket.error,
-                    ReadTimeoutError, IncompleteReadError) as e:
-                # TODO: we need a way to reset the callback if the
-                # download failed.
+            except S3_RETRYABLE_ERRORS as e:
                 logger.debug("Retrying exception caught (%s), "
                              "retrying request, (attempt %s / %s)", e, i,
                              max_attempts, exc_info=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/session.py 
new/boto3-1.3.0/boto3/session.py
--- old/boto3-1.2.3/boto3/session.py    2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/session.py    2016-03-16 01:14:40.000000000 +0100
@@ -16,9 +16,11 @@
 
 import botocore.session
 from botocore.client import Config
+from botocore.exceptions import DataNotFoundError, UnknownServiceError
 
 import boto3
 import boto3.utils
+from boto3.exceptions import ResourceNotExistsError, UnknownAPIVersionError
 
 from .resources.factory import ResourceFactory
 
@@ -90,6 +92,13 @@
         return self._session.profile or 'default'
 
     @property
+    def region_name(self):
+        """
+        The **read-only** region name.
+        """
+        return self._session.get_config_variable('region')
+
+    @property
     def events(self):
         """
         The event emitter for a session
@@ -102,7 +111,7 @@
         """
         self._loader = self._session.get_component('data_loader')
         self._loader.search_paths.append(
-             os.path.join(os.path.dirname(__file__), 'data'))
+            os.path.join(os.path.dirname(__file__), 'data'))
 
     def get_available_services(self):
         """
@@ -124,6 +133,37 @@
         """
         return self._loader.list_available_services(type_name='resources-1')
 
+    def get_available_partitions(self):
+        """Lists the available partitions
+
+        :rtype: list
+        :return: Returns a list of partition names (e.g., ["aws", "aws-cn"])
+        """
+        return self._session.get_available_partitions()
+
+    def get_available_regions(self, service_name, partition_name='aws',
+                              allow_non_regional=False):
+        """Lists the region and endpoint names of a particular partition.
+
+        :type service_name: string
+        :param service_name: Name of a service to list endpoint for (e.g., s3).
+
+        :type partition_name: string
+        :param partition_name: Name of the partition to limit endpoints to.
+            (e.g., aws for the public AWS endpoints, aws-cn for AWS China
+            endpoints, aws-us-gov for AWS GovCloud (US) Endpoints, etc.)
+
+        :type allow_non_regional: bool
+        :param allow_non_regional: Set to True to include endpoints that are
+             not regional endpoints (e.g., s3-external-1,
+             fips-us-gov-west-1, etc).
+
+        :return: Returns a list of endpoint names (e.g., ["us-east-1"]).
+        """
+        return self._session.get_available_regions(
+            service_name=service_name, partition_name=partition_name,
+            allow_non_regional=allow_non_regional)
+
     def client(self, service_name, region_name=None, api_version=None,
                use_ssl=True, verify=None, endpoint_url=None,
                aws_access_key_id=None, aws_secret_access_key=None,
@@ -200,9 +240,9 @@
             aws_session_token=aws_session_token, config=config)
 
     def resource(self, service_name, region_name=None, api_version=None,
-               use_ssl=True, verify=None, endpoint_url=None,
-               aws_access_key_id=None, aws_secret_access_key=None,
-               aws_session_token=None, config=None):
+                 use_ssl=True, verify=None, endpoint_url=None,
+                 aws_access_key_id=None, aws_secret_access_key=None,
+                 aws_session_token=None, config=None):
         """
         Create a resource service client by name.
 
@@ -268,11 +308,40 @@
 
         :return: Subclass of :py:class:`~boto3.resources.base.ServiceResource`
         """
+        try:
+            resource_model = self._loader.load_service_model(
+                service_name, 'resources-1', api_version)
+        except UnknownServiceError as e:
+            available = self.get_available_resources()
+            has_low_level_client = (
+                service_name in self.get_available_services())
+            raise ResourceNotExistsError(service_name, available,
+                                         has_low_level_client)
+        except DataNotFoundError as e:
+            # This is because we've provided an invalid API version.
+            available_api_versions = self._loader.list_api_versions(
+                service_name, 'resources-1')
+            raise UnknownAPIVersionError(
+                service_name, api_version, ', '.join(available_api_versions))
+
         if api_version is None:
+            # Even though botocore's load_service_model() can handle
+            # using the latest api_version if not provided, we need
+            # to track this api_version in boto3 in order to ensure
+            # we're pairing a resource model with a client model
+            # of the same API version.  It's possible for the latest
+            # API version of a resource model in boto3 to not be
+            # the same API version as a service model in botocore.
+            # So we need to look up the api_version if one is not
+            # provided to ensure we load the same API version of the
+            # client.
+            #
+            # Note: This is relying on the fact that
+            #   loader.load_service_model(..., api_version=None)
+            # and loader.determine_latest_version(..., 'resources-1')
+            # both load the same api version of the file.
             api_version = self._loader.determine_latest_version(
                 service_name, 'resources-1')
-        resource_model = self._loader.load_service_model(
-            service_name, 'resources-1', api_version)
 
         # Creating a new resource instance requires the low-level client
         # and service model, the resource version and resource JSON data.
@@ -343,3 +412,9 @@
             'creating-resource-class.ec2.ServiceResource',
             boto3.utils.lazy_call(
                 'boto3.ec2.createtags.inject_create_tags'))
+
+        self._session.register(
+            'creating-resource-class.ec2.Instance',
+            boto3.utils.lazy_call(
+                'boto3.ec2.deletetags.inject_delete_tags',
+                event_emitter=self.events))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3/utils.py 
new/boto3-1.3.0/boto3/utils.py
--- old/boto3-1.2.3/boto3/utils.py      2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/boto3/utils.py      2016-03-16 01:14:39.000000000 +0100
@@ -53,11 +53,15 @@
     return sys.modules[name]
 
 
-def lazy_call(full_name):
+def lazy_call(full_name, **kwargs):
+    parent_kwargs = kwargs
+
     def _handler(**kwargs):
         module, function_name = full_name.rsplit('.', 1)
         module = import_module(module)
+        kwargs.update(parent_kwargs)
         return getattr(module, function_name)(**kwargs)
+
     return _handler
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3.egg-info/PKG-INFO 
new/boto3-1.3.0/boto3.egg-info/PKG-INFO
--- old/boto3-1.2.3/boto3.egg-info/PKG-INFO     2015-12-17 23:21:36.000000000 
+0100
+++ new/boto3-1.3.0/boto3.egg-info/PKG-INFO     2016-03-16 01:14:40.000000000 
+0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: boto3
-Version: 1.2.3
+Version: 1.3.0
 Summary: The AWS SDK for Python
 Home-page: https://github.com/boto/boto3
 Author: Amazon Web Services
@@ -130,3 +130,4 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3.egg-info/SOURCES.txt 
new/boto3-1.3.0/boto3.egg-info/SOURCES.txt
--- old/boto3-1.2.3/boto3.egg-info/SOURCES.txt  2015-12-17 23:21:36.000000000 
+0100
+++ new/boto3-1.3.0/boto3.egg-info/SOURCES.txt  2016-03-16 01:14:40.000000000 
+0100
@@ -48,6 +48,7 @@
 boto3/dynamodb/types.py
 boto3/ec2/__init__.py
 boto3/ec2/createtags.py
+boto3/ec2/deletetags.py
 boto3/resources/__init__.py
 boto3/resources/action.py
 boto3/resources/base.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/boto3.egg-info/requires.txt 
new/boto3-1.3.0/boto3.egg-info/requires.txt
--- old/boto3-1.2.3/boto3.egg-info/requires.txt 2015-12-17 23:21:36.000000000 
+0100
+++ new/boto3-1.3.0/boto3.egg-info/requires.txt 2016-03-16 01:14:40.000000000 
+0100
@@ -1,4 +1,4 @@
-botocore>=1.3.0,<1.4.0
+botocore>=1.4.1,<1.5.0
 jmespath>=0.7.1,<1.0.0
 futures>=2.2.0,<4.0.0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/requirements.txt 
new/boto3-1.3.0/requirements.txt
--- old/boto3-1.2.3/requirements.txt    2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/requirements.txt    2016-03-16 01:14:39.000000000 +0100
@@ -1,5 +1,5 @@
 -e git://github.com/boto/botocore.git@develop#egg=botocore
 -e git://github.com/boto/jmespath.git@develop#egg=jmespath
 nose==1.3.3
-mock==1.0.1
+mock==1.3.0
 wheel==0.24.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/setup.cfg new/boto3-1.3.0/setup.cfg
--- old/boto3-1.2.3/setup.cfg   2015-12-17 23:21:36.000000000 +0100
+++ new/boto3-1.3.0/setup.cfg   2016-03-16 01:14:40.000000000 +0100
@@ -3,7 +3,7 @@
 
 [metadata]
 requires-dist = 
-       botocore>=1.3.0,<1.4.0
+       botocore>=1.4.1,<1.5.0
        jmespath>=0.7.1,<1.0.0
        futures>=2.2.0,<4.0.0; python_version=="2.6" or python_version=="2.7"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/boto3-1.2.3/setup.py new/boto3-1.3.0/setup.py
--- old/boto3-1.2.3/setup.py    2015-12-17 23:21:35.000000000 +0100
+++ new/boto3-1.3.0/setup.py    2016-03-16 01:14:40.000000000 +0100
@@ -15,7 +15,7 @@
 
 
 requires = [
-    'botocore>=1.3.0,<1.4.0',
+    'botocore>=1.4.1,<1.5.0',
     'jmespath>=0.7.1,<1.0.0',
 ]
 
@@ -64,5 +64,6 @@
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
     ],
 )


Reply via email to