1414
1515"""GAX wrapper for Pubsub API requests."""
1616
17+ import functools
18+
1719from google .cloud .gapic .pubsub .v1 .publisher_api import PublisherApi
1820from google .cloud .gapic .pubsub .v1 .subscriber_api import SubscriberApi
1921from google .gax import CallOptions
2022from google .gax import INITIAL_PAGE
2123from google .gax .errors import GaxError
2224from google .gax .grpc import exc_to_code
25+ from google .protobuf .json_format import MessageToDict
2326from google .pubsub .v1 .pubsub_pb2 import PubsubMessage
2427from google .pubsub .v1 .pubsub_pb2 import PushConfig
2528from grpc import insecure_channel
@@ -77,12 +80,7 @@ def list_topics(self, project, page_size=0, page_token=None):
7780 path = 'projects/%s' % (project ,)
7881 page_iter = self ._gax_api .list_topics (
7982 path , page_size = page_size , options = options )
80-
81- iter_kwargs = {}
82- if page_size : # page_size can be 0 or explicit None.
83- iter_kwargs ['max_results' ] = page_size
84- return GAXIterator (self ._client , page_iter , _item_to_topic ,
85- ** iter_kwargs )
83+ return GAXIterator (self ._client , page_iter , _item_to_topic )
8684
8785 def topic_create (self , topic_path ):
8886 """API call: create a topic
@@ -214,11 +212,8 @@ def topic_list_subscriptions(self, topic, page_size=0, page_token=None):
214212 raise NotFound (topic_path )
215213 raise
216214
217- iter_kwargs = {}
218- if page_size : # page_size can be 0 or explicit None.
219- iter_kwargs ['max_results' ] = page_size
220215 iterator = GAXIterator (self ._client , page_iter ,
221- _item_to_subscription , ** iter_kwargs )
216+ _item_to_subscription_for_topic )
222217 iterator .topic = topic
223218 return iterator
224219
@@ -228,9 +223,13 @@ class _SubscriberAPI(object):
228223
229224 :type gax_api: :class:`google.pubsub.v1.publisher_api.SubscriberApi`
230225 :param gax_api: API object used to make GAX requests.
226+
227+ :type client: :class:`~google.cloud.pubsub.client.Client`
228+ :param client: The client that owns this API object.
231229 """
232- def __init__ (self , gax_api ):
230+ def __init__ (self , gax_api , client ):
233231 self ._gax_api = gax_api
232+ self ._client = client
234233
235234 def list_subscriptions (self , project , page_size = 0 , page_token = None ):
236235 """List subscriptions for the project associated with this API.
@@ -250,22 +249,25 @@ def list_subscriptions(self, project, page_size=0, page_token=None):
250249 If not passed, the API will return the first page
251250 of subscriptions.
252251
253- :rtype: tuple, (list, str)
254- :returns: list of ``Subscription`` resource dicts, plus a
255- "next page token" string: if not None, indicates that
256- more topics can be retrieved with another call (pass that
257- value as ``page_token``).
252+ :rtype: :class:`~google.cloud.iterator.Iterator`
253+ :returns: Iterator of
254+ :class:`~google.cloud.pubsub.subscription.Subscription`
255+ accessible to the current API.
258256 """
259257 if page_token is None :
260258 page_token = INITIAL_PAGE
261259 options = CallOptions (page_token = page_token )
262260 path = 'projects/%s' % (project ,)
263261 page_iter = self ._gax_api .list_subscriptions (
264262 path , page_size = page_size , options = options )
265- subscriptions = [_subscription_pb_to_mapping (sub_pb )
266- for sub_pb in page_iter .next ()]
267- token = page_iter .page_token or None
268- return subscriptions , token
263+
264+ # We attach a mutable topics dictionary so that as topic
265+ # objects are created by Subscription.from_api_repr, they
266+ # can be re-used by other subscriptions from the same topic.
267+ topics = {}
268+ item_to_value = functools .partial (
269+ _item_to_sub_for_client , topics = topics )
270+ return GAXIterator (self ._client , page_iter , item_to_value )
269271
270272 def subscription_create (self , subscription_path , topic_path ,
271273 ack_deadline = None , push_endpoint = None ):
@@ -313,7 +315,7 @@ def subscription_create(self, subscription_path, topic_path,
313315 if exc_to_code (exc .cause ) == StatusCode .FAILED_PRECONDITION :
314316 raise Conflict (topic_path )
315317 raise
316- return _subscription_pb_to_mapping (sub_pb )
318+ return MessageToDict (sub_pb )
317319
318320 def subscription_get (self , subscription_path ):
319321 """API call: retrieve a subscription
@@ -335,7 +337,7 @@ def subscription_get(self, subscription_path):
335337 if exc_to_code (exc .cause ) == StatusCode .NOT_FOUND :
336338 raise NotFound (subscription_path )
337339 raise
338- return _subscription_pb_to_mapping (sub_pb )
340+ return MessageToDict (sub_pb )
339341
340342 def subscription_delete (self , subscription_path ):
341343 """API call: delete a subscription
@@ -474,24 +476,6 @@ def _message_pb_from_mapping(message):
474476 attributes = message ['attributes' ])
475477
476478
477- def _subscription_pb_to_mapping (sub_pb ):
478- """Helper for :meth:`list_subscriptions`, et aliae
479-
480- Performs "impedance matching" between the protobuf attrs and the keys
481- expected in the JSON API.
482- """
483- mapping = {
484- 'name' : sub_pb .name ,
485- 'topic' : sub_pb .topic ,
486- 'ackDeadlineSeconds' : sub_pb .ack_deadline_seconds ,
487- }
488- if sub_pb .push_config .push_endpoint != '' :
489- mapping ['pushConfig' ] = {
490- 'pushEndpoint' : sub_pb .push_config .push_endpoint ,
491- }
492- return mapping
493-
494-
495479def _message_pb_to_mapping (message_pb ):
496480 """Helper for :meth:`pull`, et aliae
497481
@@ -576,7 +560,7 @@ def _item_to_topic(iterator, resource):
576560 {'name' : resource .name }, iterator .client )
577561
578562
579- def _item_to_subscription (iterator , subscription_path ):
563+ def _item_to_subscription_for_topic (iterator , subscription_path ):
580564 """Convert a subscription name to the native object.
581565
582566 :type iterator: :class:`~google.cloud.iterator.Iterator`
@@ -591,3 +575,33 @@ def _item_to_subscription(iterator, subscription_path):
591575 subscription_name = subscription_name_from_path (
592576 subscription_path , iterator .client .project )
593577 return Subscription (subscription_name , iterator .topic )
578+
579+
580+ def _item_to_sub_for_client (iterator , sub_pb , topics ):
581+ """Convert a subscription protobuf to the native object.
582+
583+ .. note::
584+
585+ This method does not have the correct signature to be used as
586+ the ``item_to_value`` argument to
587+ :class:`~google.cloud.iterator.Iterator`. It is intended to be
588+ patched with a mutable topics argument that can be updated
589+ on subsequent calls. For an example, see how the method is
590+ used above in :meth:`_SubscriberAPI.list_subscriptions`.
591+
592+ :type iterator: :class:`~google.cloud.iterator.Iterator`
593+ :param iterator: The iterator that is currently in use.
594+
595+ :type sub_pb: :class:`~google.pubsub.v1.pubsub_pb2.Subscription`
596+ :param sub_pb: A subscription returned from the API.
597+
598+ :type topics: dict
599+ :param topics: A dictionary of topics to be used (and modified)
600+ as new subscriptions are created bound to topics.
601+
602+ :rtype: :class:`~google.cloud.pubsub.subscription.Subscription`
603+ :returns: The next subscription in the page.
604+ """
605+ resource = MessageToDict (sub_pb )
606+ return Subscription .from_api_repr (
607+ resource , iterator .client , topics = topics )
0 commit comments