Repository: boto/botocore
Pull request #487 (2015-03-10 23:44:41)

Add service_model property to operation_model
This makes it so that given a reference to an operation model, you can access its parent object.  Otherwise, existing code needs to track the service model when needed, despite the fact that it's accessible via an internal attribute on the operation model.

This is also needed as part of the client switchover work in the CLI.

cc @kyleknap @danielgtaylor


Patch:
diff --git a/botocore/model.py b/botocore/model.py
index 83a0b91c2..855e9a75b 100644
--- a/botocore/model.py
+++ b/botocore/model.py
@@ -324,6 +324,10 @@ class OperationModel(object):
         self.metadata = service_model.metadata
         self.http = operation_model.get('http', {})
 
+    @property
+    def service_model(self):
+        return self._service_model
+
     @CachedProperty
     def input_shape(self):
         if 'input' not in self._operation_model:
diff --git a/botocore/paginate.py b/botocore/paginate.py
index 19872b9fa..6b9abf28e 100644
--- a/botocore/paginate.py
+++ b/botocore/paginate.py
@@ -212,15 +212,7 @@ class PageIterator(object):
         for result_expression in self.result_keys:
             set_value_from_jmespath(complete_result,
                                     result_expression.expression, [])
-        for response in self:
-            page = response
-            # We want to try to catch operation object pagination
-            # and format correctly for those. They come in the form
-            # of a tuple of two elements: (http_response, parsed_responsed).
-            # We want the parsed_response as that is what the page iterator
-            # uses. We can remove it though once operation objects are removed.
-            if isinstance(response, tuple) and len(response) == 2:
-                page = response[1]
+        for _, page in self:
             # We're incrementally building the full response page
             # by page.  For each page in the response we need to
             # inject the necessary components from the page


Test patch:
diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py
index a2476e053..cbfe7e859 100644
--- a/tests/unit/test_model.py
+++ b/tests/unit/test_model.py
@@ -122,6 +122,13 @@ class TestOperationModelFromService(unittest.TestCase):
         self.assertEqual(shape.name, 'OperationNameRequest')
         self.assertEqual(list(sorted(shape.members)), ['Arg1', 'Arg2'])
 
+    def test_service_model_available_from_operation_model(self):
+        service_model = model.ServiceModel(self.model)
+        operation = service_model.operation_model('OperationName')
+        self.assertEqual(
+            operation.service_model.service_name,
+            service_model.service_name)
+
     def test_operation_output_model(self):
         service_model = model.ServiceModel(self.model)
         operation = service_model.operation_model('OperationName')
diff --git a/tests/unit/test_paginate.py b/tests/unit/test_paginate.py
index d9ecfdf03..8731aa922 100644
--- a/tests/unit/test_paginate.py
+++ b/tests/unit/test_paginate.py
@@ -183,17 +183,6 @@ class TestFuturePaginator(unittest.TestCase):
              mock.call(Marker='m2', MaxKeys=1)]
         )
 
-    def test_build_full_result_with_single_key(self):
-        responses = [
-            {"Users": ["User1"], "Marker": "m1"},
-            {"Users": ["User2"], "Marker": "m2"},
-            {"Users": ["User3"]}
-        ]
-        self.method.side_effect = responses
-        pages = self.paginator.paginate()
-        complete = pages.build_full_result()
-        self.assertEqual(complete, {'Users': ['User1', 'User2', 'User3']})
-
 
 class TestPaginatorObjectConstruction(unittest.TestCase):
     def test_pagination_delegates_to_paginator(self):