diff --git a/bigquery/google/cloud/bigquery/client.py b/bigquery/google/cloud/bigquery/client.py index 7548681c448b..785cc842e56f 100644 --- a/bigquery/google/cloud/bigquery/client.py +++ b/bigquery/google/cloud/bigquery/client.py @@ -23,7 +23,7 @@ from google.cloud.bigquery.job import LoadTableFromStorageJob from google.cloud.bigquery.job import QueryJob from google.cloud.bigquery.query import QueryResults -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator class Project(object): @@ -92,9 +92,10 @@ def list_projects(self, max_results=None, page_token=None): :returns: Iterator of :class:`~google.cloud.bigquery.client.Project` accessible to the current client. """ - return Iterator(client=self, path='/projects', - items_key='projects', item_to_value=_item_to_project, - page_token=page_token, max_results=max_results) + return HTTPIterator( + client=self, path='/projects', item_to_value=_item_to_project, + items_key='projects', page_token=page_token, + max_results=max_results) def list_datasets(self, include_all=False, max_results=None, page_token=None): @@ -123,9 +124,9 @@ def list_datasets(self, include_all=False, max_results=None, if include_all: extra_params['all'] = True path = '/projects/%s/datasets' % (self.project,) - return Iterator( - client=self, path=path, items_key='datasets', - item_to_value=_item_to_dataset, page_token=page_token, + return HTTPIterator( + client=self, path=path, item_to_value=_item_to_dataset, + items_key='datasets', page_token=page_token, max_results=max_results, extra_params=extra_params) def dataset(self, dataset_name): @@ -204,9 +205,9 @@ def list_jobs(self, max_results=None, page_token=None, all_users=None, extra_params['stateFilter'] = state_filter path = '/projects/%s/jobs' % (self.project,) - return Iterator( - client=self, path=path, items_key='jobs', - item_to_value=_item_to_job, page_token=page_token, + return HTTPIterator( + client=self, path=path, item_to_value=_item_to_job, + items_key='jobs', page_token=page_token, max_results=max_results, extra_params=extra_params) def load_table_from_storage(self, job_name, destination, *source_uris): diff --git a/core/google/cloud/_testing.py b/core/google/cloud/_testing.py index ddaeb6c8c5a4..3c01825fa6f8 100644 --- a/core/google/cloud/_testing.py +++ b/core/google/cloud/_testing.py @@ -76,15 +76,13 @@ def _make_grpc_failed_precondition(self): class _GAXPageIterator(object): - def __init__(self, items, page_token): - self._items = items - self.page_token = page_token + def __init__(self, *pages, **kwargs): + self._pages = iter(pages) + self.page_token = kwargs.get('page_token') def next(self): - if self._items is None: - raise StopIteration - items, self._items = self._items, None - return items + import six + return six.next(self._pages) __next__ = next diff --git a/core/google/cloud/iterator.py b/core/google/cloud/iterator.py index 87a938022a55..78a781188063 100644 --- a/core/google/cloud/iterator.py +++ b/core/google/cloud/iterator.py @@ -109,23 +109,19 @@ class Page(object): :type parent: :class:`Iterator` :param parent: The iterator that owns the current page. - :type response: dict - :param response: The JSON API response for a page. - - :type items_key: str - :param items_key: The dictionary key used to retrieve items - from the response. + :type items: iterable + :param items: An iterable (that also defines __len__) of items + from a raw API response. :type item_to_value: callable - :param item_to_value: Callable to convert an item from JSON - into the native object. Assumed signature - takes an :class:`Iterator` and a dictionary - holding a single item. + :param item_to_value: Callable to convert an item from the type in the + raw API response into the native object. + Assumed signature takes an :class:`Iterator` and a + raw API response with a single item. """ - def __init__(self, parent, response, items_key, item_to_value): + def __init__(self, parent, items, item_to_value): self._parent = parent - items = response.get(items_key, ()) self._num_items = len(items) self._remaining = self._num_items self._item_iter = iter(items) @@ -167,14 +163,107 @@ def next(self): class Iterator(object): + """A generic class for iterating through API list responses. + + :type client: :class:`~google.cloud.client.Client` + :param client: The client used to identify the application. + + :type item_to_value: callable + :param item_to_value: Callable to convert an item from the type in the + raw API response into the native object. + Assumed signature takes an :class:`Iterator` and a + raw API response with a single item. + + :type page_token: str + :param page_token: (Optional) A token identifying a page in a result set. + + :type max_results: int + :param max_results: (Optional) The maximum number of results to fetch. + """ + + def __init__(self, client, item_to_value, + page_token=None, max_results=None): + self._started = False + self.client = client + self._item_to_value = item_to_value + self.max_results = max_results + # The attributes below will change over the life of the iterator. + self.page_number = 0 + self.next_page_token = page_token + self.num_results = 0 + + @property + def pages(self): + """Iterator of pages in the response. + + :rtype: :class:`~types.GeneratorType` + :returns: A generator of :class:`Page` instances. + :raises ValueError: If the iterator has already been started. + """ + if self._started: + raise ValueError('Iterator has already started', self) + self._started = True + return self._page_iter(increment=True) + + def _items_iter(self): + """Iterator for each item returned.""" + for page in self._page_iter(increment=False): + for item in page: + self.num_results += 1 + yield item + + def __iter__(self): + """Iterator for each item returned. + + :rtype: :class:`~types.GeneratorType` + :returns: A generator of items from the API. + :raises ValueError: If the iterator has already been started. + """ + if self._started: + raise ValueError('Iterator has already started', self) + self._started = True + return self._items_iter() + + def _page_iter(self, increment): + """Generator of pages of API responses. + + :type increment: bool + :param increment: Flag indicating if the total number of results + should be incremented on each page. This is useful + since a page iterator will want to increment by + results per page while an items iterator will want + to increment per item. + + Yields :class:`Page` instances. + """ + page = self._next_page() + while page is not None: + self.page_number += 1 + if increment: + self.num_results += page.num_items + yield page + page = self._next_page() + + @staticmethod + def _next_page(): + """Get the next page in the iterator. + + This does nothing and is intended to be over-ridden by subclasses + to return the next :class:`Page`. + + :raises NotImplementedError: Always. + """ + raise NotImplementedError + + +class HTTPIterator(Iterator): """A generic class for iterating through Cloud JSON APIs list responses. :type client: :class:`~google.cloud.client.Client` - :param client: The client, which owns a connection to make requests. + :param client: The client used to identify the application. :type path: str - :param path: The path to query for the list of items. Defaults - to :attr:`PATH` on the current iterator class. + :param path: The path to query for the list of items. :type item_to_value: callable :param item_to_value: Callable to convert an item from JSON @@ -203,13 +292,7 @@ class Iterator(object): the :class:`Page` that was started and the dictionary containing the page response. - :type page_iter: callable - :param page_iter: (Optional) Callable to produce a pages iterator from the - current iterator. Assumed signature takes the - :class:`Iterator` that started the page. By default uses - the HTTP pages iterator. Meant to provide a custom - way to create pages (potentially with a custom - transport such as gRPC). + .. autoattribute:: pages """ _PAGE_TOKEN = 'pageToken' @@ -219,28 +302,18 @@ class Iterator(object): def __init__(self, client, path, item_to_value, items_key=DEFAULT_ITEMS_KEY, page_token=None, max_results=None, extra_params=None, - page_start=_do_nothing_page_start, page_iter=None): - self._started = False - self.client = client + page_start=_do_nothing_page_start): + super(HTTPIterator, self).__init__( + client, item_to_value, page_token=page_token, + max_results=max_results) self.path = path - self._item_to_value = item_to_value self._items_key = items_key - self.max_results = max_results self.extra_params = extra_params self._page_start = page_start - self._page_iter = None # Verify inputs / provide defaults. if self.extra_params is None: self.extra_params = {} - if page_iter is None: - self._page_iter = self._default_page_iter() - else: - self._page_iter = page_iter(self) self._verify_params() - # The attributes below will change over the life of the iterator. - self.page_number = 0 - self.next_page_token = page_token - self.num_results = 0 def _verify_params(self): """Verifies the parameters don't use any reserved parameter. @@ -253,53 +326,22 @@ def _verify_params(self): raise ValueError('Using a reserved parameter', reserved_in_use) - def _default_page_iter(self): - """Generator of pages of API responses. + def _next_page(self): + """Get the next page in the iterator. - Yields :class:`Page` instances. + :rtype: :class:`Page` + :returns: The next page in the iterator (or :data:`None` if + there are no pages left). """ - while self._has_next_page(): + if self._has_next_page(): response = self._get_next_page_response() - page = Page(self, response, self._items_key, - self._item_to_value) + items = response.get(self._items_key, ()) + page = Page(self, items, self._item_to_value) self._page_start(self, page, response) - self.num_results += page.num_items - yield page - - @property - def pages(self): - """Iterator of pages in the response. - - :rtype: :class:`~types.GeneratorType` - :returns: A generator of :class:`Page` instances. - :raises ValueError: If the iterator has already been started. - """ - if self._started: - raise ValueError('Iterator has already started', self) - self._started = True - return self._page_iter - - def _items_iter(self): - """Iterator for each item returned.""" - for page in self._page_iter: - # Decrement the total results since the pages iterator adds - # to it when each page is encountered. - self.num_results -= page.num_items - for item in page: - self.num_results += 1 - yield item - - def __iter__(self): - """Iterator for each item returned. - - :rtype: :class:`~types.GeneratorType` - :returns: A generator of items from the API. - :raises ValueError: If the iterator has already been started. - """ - if self._started: - raise ValueError('Iterator has already started', self) - self._started = True - return self._items_iter() + self.next_page_token = response.get('nextPageToken') + return page + else: + return None def _has_next_page(self): """Determines whether or not there are more pages with results. @@ -336,11 +378,53 @@ def _get_next_page_response(self): :rtype: dict :returns: The parsed JSON response of the next page's contents. """ - response = self.client.connection.api_request( + return self.client.connection.api_request( method='GET', path=self.path, query_params=self._get_query_params()) - self.page_number += 1 - self.next_page_token = response.get('nextPageToken') - return response +class GAXIterator(Iterator): + """A generic class for iterating through Cloud gRPC APIs list responses. + + :type client: :class:`~google.cloud.client.Client` + :param client: The client used to identify the application. + + :type page_iter: :class:`~google.gax.PageIterator` + :param page_iter: A GAX page iterator to be wrapped and conform to the + :class:`~google.cloud.iterator.Iterator` surface. + + :type item_to_value: callable + :param item_to_value: Callable to convert an item from a protobuf + into the native object. Assumed signature + takes an :class:`Iterator` and a single item + from the API response as a protobuf. + + :type max_results: int + :param max_results: (Optional) The maximum number of results to fetch. + + .. autoattribute:: pages + """ + + def __init__(self, client, page_iter, item_to_value, max_results=None): + super(GAXIterator, self).__init__( + client, item_to_value, page_token=page_iter.page_token, + max_results=max_results) + self._gax_page_iter = page_iter + + def _next_page(self): + """Get the next page in the iterator. + + Wraps the response from the :class:`~google.gax.PageIterator` in a + :class:`Page` instance and captures some state at each page. + + :rtype: :class:`Page` + :returns: The next page in the iterator (or :data:`None` if + there are no pages left). + """ + try: + items = six.next(self._gax_page_iter) + page = Page(self, items, self._item_to_value) + self.next_page_token = self._gax_page_iter.page_token or None + return page + except StopIteration: + return None diff --git a/core/unit_tests/test_iterator.py b/core/unit_tests/test_iterator.py index 9a7285cc20d7..fa54a13be28f 100644 --- a/core/unit_tests/test_iterator.py +++ b/core/unit_tests/test_iterator.py @@ -37,27 +37,27 @@ def _makeOne(self, *args, **kw): def test_constructor(self): parent = object() - items_key = 'potatoes' - response = {items_key: (1, 2, 3)} - page = self._makeOne(parent, response, items_key, None) + item_to_value = object() + page = self._makeOne(parent, (1, 2, 3), item_to_value) self.assertIs(page._parent, parent) self.assertEqual(page._num_items, 3) self.assertEqual(page._remaining, 3) + self.assertIs(page._item_to_value, item_to_value) def test_num_items_property(self): - page = self._makeOne(None, {}, '', None) + page = self._makeOne(None, (), None) num_items = 42 page._num_items = num_items self.assertEqual(page.num_items, num_items) def test_remaining_property(self): - page = self._makeOne(None, {}, '', None) + page = self._makeOne(None, (), None) remaining = 1337 page._remaining = remaining self.assertEqual(page.remaining, remaining) def test___iter__(self): - page = self._makeOne(None, {}, '', None) + page = self._makeOne(None, (), None) self.assertIs(iter(page), page) def test_iterator_calls__item_to_value(self): @@ -71,10 +71,8 @@ def item_to_value(self, item): self.calls += 1 return item - items_key = 'turkeys' - response = {items_key: [10, 11, 12]} parent = Parent() - page = self._makeOne(parent, response, items_key, + page = self._makeOne(parent, (10, 11, 12), Parent.item_to_value) page._remaining = 100 @@ -100,6 +98,148 @@ def _getTargetClass(self): def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) + def test_constructor(self): + connection = _Connection() + client = _Client(connection) + item_to_value = object() + token = 'ab13nceor03' + max_results = 1337 + iterator = self._makeOne(client, item_to_value, page_token=token, + max_results=max_results) + + self.assertFalse(iterator._started) + self.assertIs(iterator.client, client) + self.assertIs(iterator._item_to_value, item_to_value) + self.assertEqual(iterator.max_results, max_results) + # Changing attributes. + self.assertEqual(iterator.page_number, 0) + self.assertEqual(iterator.next_page_token, token) + self.assertEqual(iterator.num_results, 0) + + def test_pages_property(self): + iterator = self._makeOne(None, None) + self.assertFalse(iterator._started) + mock_iter = object() + incremented = [] + + def page_iter(increment): + incremented.append(increment) + return mock_iter + + iterator._page_iter = page_iter + self.assertIs(iterator.pages, mock_iter) + self.assertEqual(incremented, [True]) + # Check the side-effect. + self.assertTrue(iterator._started) + + def test_pages_property_started(self): + import types + + iterator = self._makeOne(None, None) + self.assertIsInstance(iterator.pages, types.GeneratorType) + # Make sure we cannot restart. + with self.assertRaises(ValueError): + getattr(iterator, 'pages') + + def test_pages_property_items_started(self): + import types + + iterator = self._makeOne(None, None) + self.assertIsInstance(iter(iterator), types.GeneratorType) + with self.assertRaises(ValueError): + getattr(iterator, 'pages') + + @staticmethod + def _do_nothing(parent, value): + return parent, value + + def test__items_iter(self): + import types + import six + from google.cloud.iterator import Page + + # Items to be returned. + item1 = 17 + item2 = 100 + item3 = 211 + + # Make pages from mock responses + parent = object() + page1 = Page(parent, (item1, item2), self._do_nothing) + page2 = Page(parent, (item3,), self._do_nothing) + + iterator = self._makeOne(None, None) + # Fake the page iterator on the object. + incremented = [] + + def page_iter(increment): + incremented.append(increment) + return iter((page1, page2)) + + iterator._page_iter = page_iter + items_iter = iterator._items_iter() + # Make sure it is a generator. + self.assertIsInstance(items_iter, types.GeneratorType) + + # Consume items and check the state of the iterator. + self.assertEqual(iterator.num_results, 0) + self.assertEqual(six.next(items_iter), (parent, item1)) + self.assertEqual(iterator.num_results, 1) + self.assertEqual(six.next(items_iter), (parent, item2)) + self.assertEqual(iterator.num_results, 2) + self.assertEqual(six.next(items_iter), (parent, item3)) + self.assertEqual(iterator.num_results, 3) + with self.assertRaises(StopIteration): + six.next(items_iter) + + # Make sure our page_iter() was called correctly. + self.assertEqual(incremented, [False]) + + def test___iter__(self): + iterator = self._makeOne(None, None) + self.assertFalse(iterator._started) + incremented = [] + + def page_iter(increment): + incremented.append(increment) + return iter(()) + + iterator._page_iter = page_iter + self.assertEqual(list(iterator), []) + # Check the side-effect. + self.assertTrue(iterator._started) + + def test___iter___started(self): + import types + + iterator = self._makeOne(None, None) + self.assertIsInstance(iter(iterator), types.GeneratorType) + with self.assertRaises(ValueError): + iter(iterator) + + def test___iter___pages_started(self): + import types + + iterator = self._makeOne(None, None) + self.assertIsInstance(iterator.pages, types.GeneratorType) + with self.assertRaises(ValueError): + iter(iterator) + + def test__next_page_virtual(self): + iterator = self._makeOne(None, None) + with self.assertRaises(NotImplementedError): + iterator._next_page() + + +class TestHTTPIterator(unittest.TestCase): + + def _getTargetClass(self): + from google.cloud.iterator import HTTPIterator + return HTTPIterator + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + def test_constructor(self): from google.cloud.iterator import _do_nothing_page_start @@ -128,22 +268,6 @@ def test_constructor_w_extra_param_collision(self): with self.assertRaises(ValueError): self._makeOne(client, path, None, extra_params=extra_params) - def test_constructor_non_default_page_iter(self): - connection = _Connection() - client = _Client(connection) - path = '/foo' - result = object() - called = [] - - def page_iter(iterator): - called.append(iterator) - return result - - iterator = self._makeOne(client, path, None, - page_iter=page_iter) - self.assertEqual(called, [iterator]) - self.assertIs(iterator._page_iter, result) - def test_pages_iter_empty_then_another(self): import six from google.cloud._testing import _Monkey @@ -152,7 +276,7 @@ def test_pages_iter_empty_then_another(self): items_key = 'its-key' iterator = self._makeOne(None, None, None, items_key=items_key) # Fake the next page class. - fake_page = MUT.Page(None, {}, '', None) + fake_page = MUT.Page(None, (), None) page_args = [] def dummy_response(): @@ -168,55 +292,7 @@ def dummy_page_class(*args): page = six.next(pages_iter) self.assertIs(page, fake_page) self.assertEqual( - page_args, [(iterator, {}, items_key, iterator._item_to_value)]) - - def test_pages_property(self): - import types - - iterator = self._makeOne(None, None, None) - self.assertIsInstance(iterator.pages, types.GeneratorType) - - def test_pages_property_started(self): - import types - - iterator = self._makeOne(None, None, None) - pages_iter = iterator.pages - self.assertIsInstance(pages_iter, types.GeneratorType) - with self.assertRaises(ValueError): - getattr(iterator, 'pages') - - def test_pages_property_items_started(self): - import types - - iterator = self._makeOne(None, None, None) - items_iter = iter(iterator) - self.assertIsInstance(items_iter, types.GeneratorType) - with self.assertRaises(ValueError): - getattr(iterator, 'pages') - - def test___iter__(self): - import types - - iterator = self._makeOne(None, None, None) - self.assertIsInstance(iter(iterator), types.GeneratorType) - - def test___iter___started(self): - import types - - iterator = self._makeOne(None, None, None) - iter_obj = iter(iterator) - self.assertIsInstance(iter_obj, types.GeneratorType) - with self.assertRaises(ValueError): - iter(iterator) - - def test___iter___pages_started(self): - import types - - iterator = self._makeOne(None, None, None) - pages_iter = iterator.pages - self.assertIsInstance(pages_iter, types.GeneratorType) - with self.assertRaises(ValueError): - iter(iterator) + page_args, [(iterator, (), iterator._item_to_value)]) def test_iterate(self): import six @@ -355,14 +431,121 @@ def test__get_next_page_response_new_no_token_in_response(self): iterator = self._makeOne(client, path, None) response = iterator._get_next_page_response() self.assertEqual(response['items'], [{'name': key1}, {'name': key2}]) - self.assertEqual(iterator.page_number, 1) - self.assertEqual(iterator.next_page_token, token) kw, = connection._requested self.assertEqual(kw['method'], 'GET') self.assertEqual(kw['path'], path) self.assertEqual(kw['query_params'], {}) +class TestGAXIterator(unittest.TestCase): + + def _getTargetClass(self): + from google.cloud.iterator import GAXIterator + return GAXIterator + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test_constructor(self): + client = _Client(None) + token = 'zzzyy78kl' + page_iter = SimpleIter(token) + item_to_value = object() + max_results = 1337 + iterator = self._makeOne(client, page_iter, item_to_value, + max_results=max_results) + + self.assertFalse(iterator._started) + self.assertIs(iterator.client, client) + self.assertIs(iterator._item_to_value, item_to_value) + self.assertEqual(iterator.max_results, max_results) + self.assertIs(iterator._gax_page_iter, page_iter) + # Changing attributes. + self.assertEqual(iterator.page_number, 0) + self.assertEqual(iterator.next_page_token, token) + self.assertEqual(iterator.num_results, 0) + + @staticmethod + def _do_nothing(parent, value): + return parent, value + + def test__next_page(self): + from google.cloud._testing import _GAXPageIterator + from google.cloud.iterator import Page + + # Make a mock ``google.gax.PageIterator`` + page_items = (29, 31) # Items for just one page. + page_token = '2sde98ds2s0hh' + page_iter = _GAXPageIterator(page_items, page_token=page_token) + # Wrap the GAX iterator. + iterator = self._makeOne(None, page_iter, self._do_nothing) + + page = iterator._next_page() + # First check the page token. + self.assertEqual(iterator.next_page_token, page_token) + # Then check the page. + self.assertIsInstance(page, Page) + # _do_nothing will throw the iterator in front. + expected = zip((iterator, iterator), page_items) + self.assertEqual(list(page), list(expected)) + + def test__next_page_empty(self): + from google.cloud._testing import _GAXPageIterator + + # Make a mock ``google.gax.PageIterator`` + page_iter = _GAXPageIterator() + # Wrap the GAX iterator. + iterator = self._makeOne(None, page_iter, self._do_nothing) + + page = iterator._next_page() + self.assertIsNone(page) + self.assertIsNone(iterator.next_page_token) + + def test_iterate(self): + import six + from google.cloud._testing import _GAXPageIterator + + item1 = object() + item2 = object() + item3 = object() + token1 = 'smkdme30e2e32r' + token2 = '39cm9csl123dck' + + # Make a mock ``google.gax.PageIterator`` + page1 = (item1,) + page2 = (item2, item3) + page_iter = _GAXPageIterator(page1, page2, page_token=token1) + iterator = self._makeOne(None, page_iter, self._do_nothing) + + self.assertEqual(iterator.num_results, 0) + + items_iter = iter(iterator) + val1 = six.next(items_iter) + self.assertEqual(val1, (iterator, item1)) + self.assertEqual(iterator.num_results, 1) + self.assertEqual(iterator.next_page_token, token1) + + # Before grabbing the next page, hot-swap the token + # on the ``page_iter``. + page_iter.page_token = token2 + + # Grab the next item (which will cause the next page). + val2 = six.next(items_iter) + self.assertEqual(val2, (iterator, item2)) + self.assertEqual(iterator.num_results, 2) + self.assertEqual(iterator.next_page_token, token2) + + # Grab the final item from the final / current page. + val3 = six.next(items_iter) + self.assertEqual(val3, (iterator, item3)) + self.assertEqual(iterator.num_results, 3) + # Make sure the token did not change. + self.assertEqual(iterator.next_page_token, token2) + + with self.assertRaises(StopIteration): + six.next(items_iter) + + class _Connection(object): def __init__(self, *responses): @@ -379,3 +562,9 @@ class _Client(object): def __init__(self, connection): self.connection = connection + + +class SimpleIter(object): + + def __init__(self, page_token=None): + self.page_token = page_token diff --git a/dns/google/cloud/dns/client.py b/dns/google/cloud/dns/client.py index 4637c8dae1d0..aa285562fca7 100644 --- a/dns/google/cloud/dns/client.py +++ b/dns/google/cloud/dns/client.py @@ -18,7 +18,7 @@ from google.cloud.client import JSONClient from google.cloud.dns.connection import Connection from google.cloud.dns.zone import ManagedZone -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator class Client(JSONClient): @@ -81,9 +81,10 @@ def list_zones(self, max_results=None, page_token=None): belonging to this project. """ path = '/projects/%s/managedZones' % (self.project,) - return Iterator(client=self, path=path, items_key='managedZones', - item_to_value=_item_to_zone, page_token=page_token, - max_results=max_results) + return HTTPIterator( + client=self, path=path, item_to_value=_item_to_zone, + items_key='managedZones', page_token=page_token, + max_results=max_results) def zone(self, name, dns_name=None, description=None): """Construct a zone bound to this client. diff --git a/dns/google/cloud/dns/zone.py b/dns/google/cloud/dns/zone.py index ec47a97027db..b7b5c594c320 100644 --- a/dns/google/cloud/dns/zone.py +++ b/dns/google/cloud/dns/zone.py @@ -20,7 +20,7 @@ from google.cloud.exceptions import NotFound from google.cloud.dns.changes import Changes from google.cloud.dns.resource_record_set import ResourceRecordSet -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator class ManagedZone(object): @@ -347,9 +347,9 @@ def list_resource_record_sets(self, max_results=None, page_token=None, client = self._require_client(client) path = '/projects/%s/managedZones/%s/rrsets' % ( self.project, self.name) - iterator = Iterator( - client=client, path=path, items_key='rrsets', - item_to_value=_item_to_resource_record_set, + iterator = HTTPIterator( + client=client, path=path, + item_to_value=_item_to_resource_record_set, items_key='rrsets', page_token=page_token, max_results=max_results) iterator.zone = self return iterator @@ -381,10 +381,10 @@ def list_changes(self, max_results=None, page_token=None, client=None): client = self._require_client(client) path = '/projects/%s/managedZones/%s/changes' % ( self.project, self.name) - iterator = Iterator( - client=client, path=path, items_key='changes', - item_to_value=_item_to_changes, - page_token=page_token, max_results=max_results) + iterator = HTTPIterator( + client=client, path=path, item_to_value=_item_to_changes, + items_key='changes', page_token=page_token, + max_results=max_results) iterator.zone = self return iterator diff --git a/logging/unit_tests/test__gax.py b/logging/unit_tests/test__gax.py index f0c3ec6595d3..1fd5d2fc9a22 100644 --- a/logging/unit_tests/test__gax.py +++ b/logging/unit_tests/test__gax.py @@ -69,7 +69,7 @@ def test_list_entries_no_paging(self): resource=resource_pb, timestamp=timestamp_pb, text_payload=TEXT) - response = _GAXPageIterator([entry_pb], TOKEN) + response = _GAXPageIterator([entry_pb], page_token=TOKEN) gax_api = _GAXLoggingAPI(_list_log_entries_response=response) api = self._makeOne(gax_api) @@ -110,7 +110,7 @@ def _list_entries_with_paging_helper(self, payload, struct_pb): resource=resource_pb, timestamp=timestamp_pb, json_payload=struct_pb) - response = _GAXPageIterator([entry_pb], NEW_TOKEN) + response = _GAXPageIterator([entry_pb], page_token=NEW_TOKEN) gax_api = _GAXLoggingAPI(_list_log_entries_response=response) api = self._makeOne(gax_api) @@ -237,7 +237,7 @@ def test_list_entries_with_extra_properties(self): entry_pb = self._make_log_entry_with_extras( LABELS, IID, bool_type_url, NOW) - response = _GAXPageIterator([entry_pb], NEW_TOKEN) + response = _GAXPageIterator([entry_pb], page_token=NEW_TOKEN) gax_api = _GAXLoggingAPI(_list_log_entries_response=response) api = self._makeOne(gax_api) @@ -598,7 +598,7 @@ def test_list_sinks_no_paging(self): sink_pb = LogSink(name=self.SINK_PATH, destination=self.DESTINATION_URI, filter=self.FILTER) - response = _GAXPageIterator([sink_pb], TOKEN) + response = _GAXPageIterator([sink_pb], page_token=TOKEN) gax_api = _GAXSinksAPI(_list_sinks_response=response) api = self._makeOne(gax_api) @@ -626,7 +626,7 @@ def test_list_sinks_w_paging(self): sink_pb = LogSink(name=self.SINK_PATH, destination=self.DESTINATION_URI, filter=self.FILTER) - response = _GAXPageIterator([sink_pb], None) + response = _GAXPageIterator([sink_pb]) gax_api = _GAXSinksAPI(_list_sinks_response=response) api = self._makeOne(gax_api) @@ -813,7 +813,7 @@ def test_list_metrics_no_paging(self): metric_pb = LogMetric(name=self.METRIC_PATH, description=self.DESCRIPTION, filter=self.FILTER) - response = _GAXPageIterator([metric_pb], TOKEN) + response = _GAXPageIterator([metric_pb], page_token=TOKEN) gax_api = _GAXMetricsAPI(_list_log_metrics_response=response) api = self._makeOne(gax_api) @@ -841,7 +841,7 @@ def test_list_metrics_w_paging(self): metric_pb = LogMetric(name=self.METRIC_PATH, description=self.DESCRIPTION, filter=self.FILTER) - response = _GAXPageIterator([metric_pb], None) + response = _GAXPageIterator([metric_pb]) gax_api = _GAXMetricsAPI(_list_log_metrics_response=response) api = self._makeOne(gax_api) diff --git a/pubsub/google/cloud/pubsub/_gax.py b/pubsub/google/cloud/pubsub/_gax.py index 1a39c0078b69..b6b357cbdc04 100644 --- a/pubsub/google/cloud/pubsub/_gax.py +++ b/pubsub/google/cloud/pubsub/_gax.py @@ -14,8 +14,6 @@ """GAX wrapper for Pubsub API requests.""" -import functools - from google.cloud.gapic.pubsub.v1.publisher_api import PublisherApi from google.cloud.gapic.pubsub.v1.subscriber_api import SubscriberApi from google.gax import CallOptions @@ -31,14 +29,10 @@ from google.cloud._helpers import _pb_timestamp_to_rfc3339 from google.cloud.exceptions import Conflict from google.cloud.exceptions import NotFound -from google.cloud.iterator import Iterator -from google.cloud.iterator import Page +from google.cloud.iterator import GAXIterator from google.cloud.pubsub.topic import Topic -_FAKE_ITEMS_KEY = 'not-a-key' - - class _PublisherAPI(object): """Helper mapping publisher-related APIs. @@ -81,11 +75,12 @@ def list_topics(self, project, page_size=0, page_token=None): path = 'projects/%s' % (project,) page_iter = self._gax_api.list_topics( path, page_size=page_size, options=options) - page_iter = functools.partial(_recast_page_iterator, page_iter) - return Iterator(client=self._client, path=path, - item_to_value=_item_to_topic, - page_iter=page_iter) + iter_kwargs = {} + if page_size: # page_size can be 0 or explicit None. + iter_kwargs['max_results'] = page_size + return GAXIterator(self._client, page_iter, _item_to_topic, + **iter_kwargs) def topic_create(self, topic_path): """API call: create a topic @@ -572,26 +567,3 @@ def _item_to_topic(iterator, resource): """ return Topic.from_api_repr( {'name': resource.name}, iterator.client) - - -def _recast_page_iterator(page_iter, iterator): - """Wrap GAX pages generator. - - In particular, wrap each page and capture some state from the - GAX iterator. - - Yields :class:`~google.cloud.iterator.Page` instances - - :type page_iter: :class:`~google.gax.PageIterator` - :param page_iter: The iterator to wrap. - - :type iterator: :class:`~google.cloud.iterator.Iterator` - :param iterator: The iterator that owns each page. - """ - for items in page_iter: - fake_response = {_FAKE_ITEMS_KEY: items} - page = Page( - iterator, fake_response, _FAKE_ITEMS_KEY, _item_to_topic) - iterator.next_page_token = page_iter.page_token or None - iterator.num_results += page.num_items - yield page diff --git a/pubsub/google/cloud/pubsub/connection.py b/pubsub/google/cloud/pubsub/connection.py index 722b28102dfa..53f57f6c7435 100644 --- a/pubsub/google/cloud/pubsub/connection.py +++ b/pubsub/google/cloud/pubsub/connection.py @@ -19,7 +19,7 @@ from google.cloud import connection as base_connection from google.cloud.environment_vars import PUBSUB_EMULATOR -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator from google.cloud.pubsub.topic import Topic @@ -134,9 +134,10 @@ def list_topics(self, project, page_size=None, page_token=None): extra_params['pageSize'] = page_size path = '/projects/%s/topics' % (project,) - return Iterator(client=self._client, path=path, - items_key='topics', item_to_value=_item_to_topic, - page_token=page_token, extra_params=extra_params) + return HTTPIterator( + client=self._client, path=path, item_to_value=_item_to_topic, + items_key='topics', page_token=page_token, + extra_params=extra_params) def topic_create(self, topic_path): """API call: create a topic diff --git a/pubsub/unit_tests/test__gax.py b/pubsub/unit_tests/test__gax.py index 907bdecb5c89..06e2b87adb78 100644 --- a/pubsub/unit_tests/test__gax.py +++ b/pubsub/unit_tests/test__gax.py @@ -61,7 +61,8 @@ def test_list_topics_no_paging(self): from google.cloud.pubsub.topic import Topic TOKEN = 'TOKEN' - response = _GAXPageIterator([_TopicPB(self.TOPIC_PATH)], TOKEN) + response = _GAXPageIterator([_TopicPB(self.TOPIC_PATH)], + page_token=TOKEN) gax_api = _GAXPublisherAPI(_list_topics_response=response) client = _Client(self.PROJECT) api = self._makeOne(gax_api, client) @@ -90,7 +91,7 @@ def test_list_topics_with_paging(self): TOKEN = 'TOKEN' NEW_TOKEN = 'NEW_TOKEN' response = _GAXPageIterator( - [_TopicPB(self.TOPIC_PATH)], NEW_TOKEN) + [_TopicPB(self.TOPIC_PATH)], page_token=NEW_TOKEN) gax_api = _GAXPublisherAPI(_list_topics_response=response) client = _Client(self.PROJECT) api = self._makeOne(gax_api, client) @@ -291,8 +292,8 @@ def test_topic_publish_error(self): def test_topic_list_subscriptions_no_paging(self): from google.gax import INITIAL_PAGE from google.cloud._testing import _GAXPageIterator - response = _GAXPageIterator([ - {'name': self.SUB_PATH, 'topic': self.TOPIC_PATH}], None) + response = _GAXPageIterator( + [{'name': self.SUB_PATH, 'topic': self.TOPIC_PATH}]) gax_api = _GAXPublisherAPI(_list_topic_subscriptions_response=response) client = _Client(self.PROJECT) api = self._makeOne(gax_api, client) @@ -318,8 +319,9 @@ def test_topic_list_subscriptions_with_paging(self): SIZE = 23 TOKEN = 'TOKEN' NEW_TOKEN = 'NEW_TOKEN' - response = _GAXPageIterator([ - {'name': self.SUB_PATH, 'topic': self.TOPIC_PATH}], NEW_TOKEN) + response = _GAXPageIterator( + [{'name': self.SUB_PATH, 'topic': self.TOPIC_PATH}], + page_token=NEW_TOKEN) gax_api = _GAXPublisherAPI(_list_topic_subscriptions_response=response) client = _Client(self.PROJECT) api = self._makeOne(gax_api, client) @@ -390,8 +392,10 @@ def test_ctor(self): def test_list_subscriptions_no_paging(self): from google.gax import INITIAL_PAGE from google.cloud._testing import _GAXPageIterator - response = _GAXPageIterator([_SubscriptionPB( - self.SUB_PATH, self.TOPIC_PATH, self.PUSH_ENDPOINT, 0)], None) + + sub_pb = _SubscriptionPB( + self.SUB_PATH, self.TOPIC_PATH, self.PUSH_ENDPOINT, 0) + response = _GAXPageIterator([sub_pb]) gax_api = _GAXSubscriberAPI(_list_subscriptions_response=response) api = self._makeOne(gax_api) @@ -417,8 +421,9 @@ def test_list_subscriptions_with_paging(self): SIZE = 23 TOKEN = 'TOKEN' NEW_TOKEN = 'NEW_TOKEN' - response = _GAXPageIterator([_SubscriptionPB( - self.SUB_PATH, self.TOPIC_PATH, self.PUSH_ENDPOINT, 0)], NEW_TOKEN) + sub_pb = _SubscriptionPB( + self.SUB_PATH, self.TOPIC_PATH, self.PUSH_ENDPOINT, 0) + response = _GAXPageIterator([sub_pb], page_token=NEW_TOKEN) gax_api = _GAXSubscriberAPI(_list_subscriptions_response=response) api = self._makeOne(gax_api) diff --git a/pubsub/unit_tests/test_client.py b/pubsub/unit_tests/test_client.py index 5b12aee800e3..4f4d597591fa 100644 --- a/pubsub/unit_tests/test_client.py +++ b/pubsub/unit_tests/test_client.py @@ -79,12 +79,11 @@ def __init__(self, _wrapped, client): self._client = client creds = _Credentials() - client = self._makeOne(project=self.PROJECT, credentials=creds) - with _Monkey(MUT, - _USE_GAX=True, + with _Monkey(MUT, _USE_GAX=True, make_gax_publisher_api=_generated_api, GAXPublisherAPI=_GaxPublisherAPI): + client = self._makeOne(project=self.PROJECT, credentials=creds) api = client.publisher_api self.assertIsInstance(api, _GaxPublisherAPI) @@ -131,12 +130,11 @@ def __init__(self, _wrapped): self._wrapped = _wrapped creds = _Credentials() - client = self._makeOne(project=self.PROJECT, credentials=creds) - with _Monkey(MUT, - _USE_GAX=True, + with _Monkey(MUT, _USE_GAX=True, make_gax_subscriber_api=_generated_api, GAXSubscriberAPI=_GaxSubscriberAPI): + client = self._makeOne(project=self.PROJECT, credentials=creds) api = client.subscriber_api self.assertIsInstance(api, _GaxSubscriberAPI) diff --git a/resource_manager/google/cloud/resource_manager/client.py b/resource_manager/google/cloud/resource_manager/client.py index 0c0341948aab..dca9446acbb6 100644 --- a/resource_manager/google/cloud/resource_manager/client.py +++ b/resource_manager/google/cloud/resource_manager/client.py @@ -16,7 +16,7 @@ from google.cloud.client import Client as BaseClient -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator from google.cloud.resource_manager.connection import Connection from google.cloud.resource_manager.project import Project @@ -154,7 +154,7 @@ def list_projects(self, filter_params=None, page_size=None): if filter_params is not None: extra_params['filter'] = filter_params - return Iterator( + return HTTPIterator( client=self, path='/projects', item_to_value=_item_to_project, items_key='projects', extra_params=extra_params) diff --git a/resource_manager/unit_tests/test_client.py b/resource_manager/unit_tests/test_client.py index cdc4159dd8ab..dd40f8b6877f 100644 --- a/resource_manager/unit_tests/test_client.py +++ b/resource_manager/unit_tests/test_client.py @@ -78,7 +78,7 @@ def test_fetch_project(self): self.assertEqual(project.labels, labels) def test_list_projects_return_type(self): - from google.cloud.iterator import Iterator + from google.cloud.iterator import HTTPIterator credentials = _Credentials() client = self._makeOne(credentials=credentials) @@ -86,7 +86,7 @@ def test_list_projects_return_type(self): client.connection = _Connection({}) results = client.list_projects() - self.assertIsInstance(results, Iterator) + self.assertIsInstance(results, HTTPIterator) def test_list_projects_no_paging(self): credentials = _Credentials() @@ -222,7 +222,7 @@ def test_page_empty_response(self): credentials = _Credentials() client = self._makeOne(credentials=credentials) iterator = client.list_projects() - page = Page(iterator, {}, iterator._items_key, None) + page = Page(iterator, (), None) iterator._page = page self.assertEqual(page.num_items, 0) self.assertEqual(page.remaining, 0) diff --git a/runtimeconfig/google/cloud/runtimeconfig/config.py b/runtimeconfig/google/cloud/runtimeconfig/config.py index b73897dd02b1..7a259df23f09 100644 --- a/runtimeconfig/google/cloud/runtimeconfig/config.py +++ b/runtimeconfig/google/cloud/runtimeconfig/config.py @@ -17,7 +17,7 @@ from google.cloud.exceptions import NotFound from google.cloud.runtimeconfig._helpers import config_name_from_full_name from google.cloud.runtimeconfig.variable import Variable -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator class Config(object): @@ -238,9 +238,9 @@ def list_variables(self, page_size=None, page_token=None, client=None): belonging to this project. """ path = '%s/variables' % (self.path,) - iterator = Iterator( + iterator = HTTPIterator( client=self._require_client(client), path=path, - items_key='variables', item_to_value=_item_to_variable, + item_to_value=_item_to_variable, items_key='variables', page_token=page_token, max_results=page_size) iterator._MAX_RESULTS = 'pageSize' iterator.config = self diff --git a/runtimeconfig/unit_tests/test_client.py b/runtimeconfig/unit_tests/test_client.py index 350a825f6cdf..943385230e8b 100644 --- a/runtimeconfig/unit_tests/test_client.py +++ b/runtimeconfig/unit_tests/test_client.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - import unittest diff --git a/runtimeconfig/unit_tests/test_config.py b/runtimeconfig/unit_tests/test_config.py index 5eed1482539d..d58184bd58b6 100644 --- a/runtimeconfig/unit_tests/test_config.py +++ b/runtimeconfig/unit_tests/test_config.py @@ -14,9 +14,6 @@ import unittest -from google.cloud._helpers import _rfc3339_to_datetime -from google.cloud.runtimeconfig._helpers import config_name_from_full_name - class TestConfig(unittest.TestCase): PROJECT = 'PROJECT' @@ -31,6 +28,9 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _verifyResourceProperties(self, config, resource): + from google.cloud.runtimeconfig._helpers import ( + config_name_from_full_name) + if 'name' in resource: self.assertEqual(config.full_name, resource['name']) self.assertEqual( @@ -51,7 +51,7 @@ def test_ctor_w_no_name(self): client = _Client(project=self.PROJECT) config = self._makeOne(name=None, client=client) with self.assertRaises(ValueError): - _ = config.full_name + getattr(config, 'full_name') def test_exists_miss_w_bound_client(self): conn = _Connection() @@ -143,6 +143,8 @@ def test_variable(self): self.assertEqual(len(conn._requested), 0) def test_get_variable_w_bound_client(self): + from google.cloud._helpers import _rfc3339_to_datetime + VARIABLE_NAME = 'my-variable/abcd' VARIABLE_PATH = '%s/variables/%s' % (self.CONFIG_PATH, VARIABLE_NAME) RESOURCE = { @@ -178,6 +180,8 @@ def test_get_variable_w_notfound(self): self.assertIsNone(variable) def test_get_variable_w_alternate_client(self): + from google.cloud._helpers import _rfc3339_to_datetime + VARIABLE_NAME = 'my-variable/abcd' VARIABLE_PATH = '%s/variables/%s' % (self.CONFIG_PATH, VARIABLE_NAME) RESOURCE = { @@ -230,6 +234,7 @@ def test_list_variables_empty(self): def test_list_variables_defaults(self): import six + from google.cloud._helpers import _rfc3339_to_datetime from google.cloud.runtimeconfig.variable import Variable VARIABLE_1 = 'variable-one' @@ -273,6 +278,7 @@ def test_list_variables_defaults(self): def test_list_variables_explicit(self): import six + from google.cloud._helpers import _rfc3339_to_datetime from google.cloud.runtimeconfig.variable import Variable VARIABLE_1 = 'variable-one' diff --git a/runtimeconfig/unit_tests/test_variable.py b/runtimeconfig/unit_tests/test_variable.py index 5f9d441884c8..dc5df9a0d716 100644 --- a/runtimeconfig/unit_tests/test_variable.py +++ b/runtimeconfig/unit_tests/test_variable.py @@ -12,12 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import base64 import unittest -from google.cloud.runtimeconfig.config import Config -from google.cloud._helpers import _rfc3339_to_datetime - class TestVariable(unittest.TestCase): PROJECT = 'PROJECT' @@ -34,6 +30,9 @@ def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _verifyResourceProperties(self, variable, resource): + import base64 + from google.cloud._helpers import _rfc3339_to_datetime + if 'name' in resource: self.assertEqual(variable.full_name, resource['name']) @@ -54,6 +53,8 @@ def _verifyResourceProperties(self, variable, resource): self.assertIsNone(variable.update_time) def test_ctor(self): + from google.cloud.runtimeconfig.config import Config + client = _Client(project=self.PROJECT) config = Config(name=self.CONFIG_NAME, client=client) variable = self._makeOne(name=self.VARIABLE_NAME, config=config) @@ -63,13 +64,17 @@ def test_ctor(self): self.assertIs(variable.client, client) def test_ctor_w_no_name(self): + from google.cloud.runtimeconfig.config import Config + client = _Client(project=self.PROJECT) config = Config(name=self.CONFIG_NAME, client=client) variable = self._makeOne(name=None, config=config) with self.assertRaises(ValueError): - _ = variable.full_name + getattr(variable, 'full_name') def test_exists_miss_w_bound_client(self): + from google.cloud.runtimeconfig.config import Config + conn = _Connection() client = _Client(project=self.PROJECT, connection=conn) config = Config(name=self.CONFIG_NAME, client=client) @@ -84,6 +89,8 @@ def test_exists_miss_w_bound_client(self): self.assertEqual(req['query_params'], {'fields': 'name'}) def test_exists_hit_w_alternate_client(self): + from google.cloud.runtimeconfig.config import Config + conn1 = _Connection() CLIENT1 = _Client(project=self.PROJECT, connection=conn1) CONFIG1 = Config(name=self.CONFIG_NAME, client=CLIENT1) @@ -101,6 +108,8 @@ def test_exists_hit_w_alternate_client(self): self.assertEqual(req['query_params'], {'fields': 'name'}) def test_reload_w_bound_client(self): + from google.cloud.runtimeconfig.config import Config + RESOURCE = { 'name': self.PATH, 'value': 'bXktdmFyaWFibGUtdmFsdWU=', # base64 my-variable-value @@ -121,6 +130,8 @@ def test_reload_w_bound_client(self): self._verifyResourceProperties(variable, RESOURCE) def test_reload_w_empty_resource(self): + from google.cloud.runtimeconfig.config import Config + RESOURCE = {} conn = _Connection(RESOURCE) client = _Client(project=self.PROJECT, connection=conn) @@ -139,6 +150,8 @@ def test_reload_w_empty_resource(self): self._verifyResourceProperties(variable, RESOURCE) def test_reload_w_alternate_client(self): + from google.cloud.runtimeconfig.config import Config + RESOURCE = { 'name': self.PATH, 'value': 'bXktdmFyaWFibGUtdmFsdWU=', # base64 my-variable-value diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py index 9f8da3e75b7e..b2ffafb1311d 100644 --- a/storage/google/cloud/storage/bucket.py +++ b/storage/google/cloud/storage/bucket.py @@ -20,7 +20,7 @@ from google.cloud._helpers import _rfc3339_to_datetime from google.cloud.exceptions import NotFound -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator from google.cloud.storage._helpers import _PropertyMixin from google.cloud.storage._helpers import _scalar_property from google.cloud.storage.acl import BucketACL @@ -306,7 +306,7 @@ def list_blobs(self, max_results=None, page_token=None, prefix=None, client = self._require_client(client) path = self.path + '/o' - iterator = Iterator( + iterator = HTTPIterator( client=client, path=path, item_to_value=_item_to_blob, page_token=page_token, max_results=max_results, extra_params=extra_params, page_start=_blobs_page_start) diff --git a/storage/google/cloud/storage/client.py b/storage/google/cloud/storage/client.py index 721f5dd962ec..4255c43720c4 100644 --- a/storage/google/cloud/storage/client.py +++ b/storage/google/cloud/storage/client.py @@ -18,7 +18,7 @@ from google.cloud._helpers import _LocalStack from google.cloud.client import JSONClient from google.cloud.exceptions import NotFound -from google.cloud.iterator import Iterator +from google.cloud.iterator import HTTPIterator from google.cloud.storage.batch import Batch from google.cloud.storage.bucket import Bucket from google.cloud.storage.connection import Connection @@ -265,7 +265,7 @@ def list_buckets(self, max_results=None, page_token=None, prefix=None, if fields is not None: extra_params['fields'] = fields - return Iterator( + return HTTPIterator( client=self, path='/b', item_to_value=_item_to_bucket, page_token=page_token, max_results=max_results, extra_params=extra_params) diff --git a/storage/unit_tests/test_bucket.py b/storage/unit_tests/test_bucket.py index 263a09e9df09..53a79884f0b6 100644 --- a/storage/unit_tests/test_bucket.py +++ b/storage/unit_tests/test_bucket.py @@ -984,7 +984,7 @@ def test_page_empty_response(self): name = 'name' bucket = self._makeOne(client=client, name=name) iterator = bucket.list_blobs() - page = Page(iterator, {}, iterator._items_key, None) + page = Page(iterator, (), None) iterator._page = page blobs = list(page) self.assertEqual(blobs, []) @@ -1024,6 +1024,7 @@ def test_cumulative_prefixes(self): response1 = { 'items': [{'name': BLOB_NAME}], 'prefixes': ['foo'], + 'nextPageToken': 's39rmf9', } response2 = { 'items': [], diff --git a/storage/unit_tests/test_client.py b/storage/unit_tests/test_client.py index 2b55fff71968..61eeda051db5 100644 --- a/storage/unit_tests/test_client.py +++ b/storage/unit_tests/test_client.py @@ -376,7 +376,7 @@ def test_page_empty_response(self): credentials = _Credentials() client = self._makeOne(project=project, credentials=credentials) iterator = client.list_buckets() - page = Page(iterator, {}, iterator._items_key, None) + page = Page(iterator, (), None) iterator._page = page self.assertEqual(list(page), [])