Repository: boto/botocore
Pull request #491 (2015-03-12 20:49:29)

Added a PaginatorModel
This pretty much provides a parallel to the ``WaiterModel`` in waiters.py and ``session.get_waiter_model()`` for paginators. It is pretty light-weight right now. We can add to this as we see needed.

cc @jamesls @danielgtaylor 



coveralls: 
[![Coverage Status](https://coveralls.io/builds/2102509/badge)](https://coveralls.io/builds/2102509)

Coverage increased (+0.02%) to 94.58% when pulling **d3bb8ebeb8984ebd089156cbb0b33c52aadead5f on kyleknap:pagination-model** into **486fbe71efb1199cc947001e59ea5b2767189b0a on boto:develop**.

jamesls: :shipit:

Patch:
diff --git a/botocore/paginate.py b/botocore/paginate.py
index 19872b9fa..59f28b119 100644
--- a/botocore/paginate.py
+++ b/botocore/paginate.py
@@ -19,6 +19,19 @@ from botocore.compat import zip
 from botocore.utils import set_value_from_jmespath, merge_dicts
 
 
+class PaginatorModel(object):
+    def __init__(self, paginator_config):
+        self._paginator_config = paginator_config['pagination']
+
+    def get_paginator(self, operation_name):
+        try:
+            single_paginator_config = self._paginator_config[operation_name]
+        except KeyError:
+            raise ValueError("Paginator for operation does not exist: %s"
+                             % operation_name)
+        return single_paginator_config
+
+
 class PageIterator(object):
     def __init__(self, method, input_token, output_token, more_results,
                  result_keys, non_aggregate_keys, limit_key, max_items,
diff --git a/botocore/session.py b/botocore/session.py
index f7d473ffe..d1d721cf7 100644
--- a/botocore/session.py
+++ b/botocore/session.py
@@ -35,6 +35,7 @@ from botocore.provider import get_provider
 from botocore.parsers import ResponseParserFactory
 from botocore import regions
 from botocore.model import ServiceModel
+from botocore import paginate
 import botocore.service
 from botocore import waiter
 from botocore import retryhandler, translate
@@ -510,6 +511,14 @@ class Session(object):
         waiter_config = loader.load_data(waiter_path)
         return waiter.WaiterModel(waiter_config)
 
+    def get_paginator_model(self, service_name, api_version=None):
+        loader = self.get_component('data_loader')
+        latest = loader.determine_latest('%s/%s' % (
+            self.provider.name, service_name), api_version)
+        paginator_path = latest.replace('.normal', '.paginators')
+        paginator_config = loader.load_data(paginator_path)
+        return paginate.PaginatorModel(paginator_config)
+
     def get_service_data(self, service_name, api_version=None):
         """
         Retrieve the fully merged data associated with a service.


Test patch:
diff --git a/tests/unit/test_paginate.py b/tests/unit/test_paginate.py
index d9ecfdf03..ee485bc5f 100644
--- a/tests/unit/test_paginate.py
+++ b/tests/unit/test_paginate.py
@@ -14,11 +14,38 @@
 from tests import unittest
 from botocore.paginate import Paginator as FuturePaginator
 from botocore.paginate import DeprecatedPaginator as Paginator
+from botocore.paginate import PaginatorModel
 from botocore.exceptions import PaginationError
 from botocore.operation import Operation
 
 import mock
 
+
+class TestPaginatorModel(unittest.TestCase):
+    def setUp(self):
+        self.paginator_config = {}
+        self.paginator_config['pagination'] = {
+            'ListFoos': {
+                'output_token': 'NextToken',
+                'input_token': 'NextToken',
+                'result_key': 'Foo'
+            }
+        }
+        self.paginator_model = PaginatorModel(self.paginator_config)
+
+    def test_get_paginator(self):
+        paginator_config = self.paginator_model.get_paginator('ListFoos')
+        self.assertEqual(
+            paginator_config,
+            {'output_token': 'NextToken', 'input_token': 'NextToken',
+             'result_key': 'Foo'}
+        )
+
+    def test_get_paginator_no_exists(self):
+        with self.assertRaises(ValueError):
+            paginator_config = self.paginator_model.get_paginator('ListBars')
+
+
 # TODO: FuturePaginator tests should be merged into tests that used the renamed
 # Deprecated paginators when we completely remove the Deprecated
 # paginator class and make all of the tests use the actual Paginator class
diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py
index 0bd620272..9353a8414 100644
--- a/tests/unit/test_session.py
+++ b/tests/unit/test_session.py
@@ -27,6 +27,7 @@ from botocore.model import ServiceModel
 from botocore import client
 from botocore.hooks import HierarchicalEmitter
 from botocore.waiter import WaiterModel
+from botocore.paginate import PaginatorModel
 
 
 class BaseSessionTest(unittest.TestCase):
@@ -346,6 +347,22 @@ class TestGetServiceModel(BaseSessionTest):
         self.assertEqual(model.service_name, 'made_up')
 
 
+class TestGetPaginatorModel(BaseSessionTest):
+    def test_get_paginator_model(self):
+        loader = mock.Mock()
+        loader.determine_latest.return_value = 'aws/foo/2014-01-01.normal.json'
+        loader.load_data.return_value = {"pagination": {}}
+        self.session.register_component('data_loader', loader)
+
+        model = self.session.get_paginator_model('foo')
+
+        # Verify we get a PaginatorModel back
+        self.assertIsInstance(model, PaginatorModel)
+        # Verify we called the loader correctly.
+        loader.load_data.assert_called_with(
+            'aws/foo/2014-01-01.paginators.json')
+
+
 class TestGetWaiterModel(BaseSessionTest):
     def test_get_waiter_model(self):
         loader = mock.Mock()