Repository: googleapis/google-cloud-python

Issue:
Pubsub.list_topics fails when there are no topics
[Offending line](https://github.com/GoogleCloudPlatform/gcloud-python/blob/0910f9979a45af8cc2826dd4c6ff38d9efa5ccec/gcloud/pubsub/client.py#L80). Reproduce via:

``` python
client = pubsub.Client()
>>> client.list_topics()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "gcloud/pubsub/client.py", line 80, in list_topics
    for resource in resp['topics']]
KeyError: 'topics'
```

@tseaver ISTM we should locate all instances where we assume a key is present and just protect against this. The time between releases behooves us to be "protective" of users. (I realize that we've usually done it this way based on documented outputs.)



Discussion:
Found while reproducing #1438


Resolving patch:
diff --git a/gcloud/pubsub/client.py b/gcloud/pubsub/client.py
--- a/gcloud/pubsub/client.py
+++ b/gcloud/pubsub/client.py
@@ -77,7 +77,7 @@ def list_topics(self, page_size=None, page_token=None):
         resp = self.connection.api_request(method='GET', path=path,
                                            query_params=params)
         topics = [Topic.from_api_repr(resource, self)
-                  for resource in resp['topics']]
+                  for resource in resp.get('topics', ())]
         return topics, resp.get('nextPageToken')
 
     def list_subscriptions(self, page_size=None, page_token=None,
@@ -128,7 +128,7 @@ def list_subscriptions(self, page_size=None, page_token=None,
         topics = {}
         subscriptions = [Subscription.from_api_repr(resource, self,
                                                     topics=topics)
-                         for resource in resp['subscriptions']]
+                         for resource in resp.get('subscriptions', ())]
         return subscriptions, resp.get('nextPageToken')
 
     def topic(self, name, timestamp_messages=False):


Test patch:
diff --git a/gcloud/pubsub/test_client.py b/gcloud/pubsub/test_client.py
--- a/gcloud/pubsub/test_client.py
+++ b/gcloud/pubsub/test_client.py
@@ -82,6 +82,27 @@ def test_list_topics_with_paging(self):
         self.assertEqual(req['query_params'],
                          {'pageSize': SIZE, 'pageToken': TOKEN1})
 
+    def test_list_topics_missing_key(self):
+        PROJECT = 'PROJECT'
+        CREDS = _Credentials()
+
+        CLIENT_OBJ = self._makeOne(project=PROJECT, credentials=CREDS)
+
+        RETURNED = {}
+        # Replace the connection on the client with one of our own.
+        CLIENT_OBJ.connection = _Connection(RETURNED)
+
+        # Execute request.
+        topics, next_page_token = CLIENT_OBJ.list_topics()
+        # Test values are correct.
+        self.assertEqual(len(topics), 0)
+        self.assertEqual(next_page_token, None)
+        self.assertEqual(len(CLIENT_OBJ.connection._requested), 1)
+        req = CLIENT_OBJ.connection._requested[0]
+        self.assertEqual(req['method'], 'GET')
+        self.assertEqual(req['path'], '/projects/%s/topics' % PROJECT)
+        self.assertEqual(req['query_params'], {})
+
     def test_list_subscriptions_no_paging(self):
         from gcloud.pubsub.subscription import Subscription
         PROJECT = 'PROJECT'
@@ -154,6 +175,27 @@ def test_list_subscriptions_with_paging(self):
         self.assertEqual(req['query_params'],
                          {'pageSize': SIZE, 'pageToken': TOKEN1})
 
+    def test_list_subscriptions_w_missing_key(self):
+        PROJECT = 'PROJECT'
+        CREDS = _Credentials()
+
+        CLIENT_OBJ = self._makeOne(project=PROJECT, credentials=CREDS)
+
+        RETURNED = {}
+        # Replace the connection on the client with one of our own.
+        CLIENT_OBJ.connection = _Connection(RETURNED)
+
+        # Execute request.
+        subscriptions, next_page_token = CLIENT_OBJ.list_subscriptions()
+        # Test values are correct.
+        self.assertEqual(len(subscriptions), 0)
+        self.assertEqual(next_page_token, None)
+        self.assertEqual(len(CLIENT_OBJ.connection._requested), 1)
+        req = CLIENT_OBJ.connection._requested[0]
+        self.assertEqual(req['method'], 'GET')
+        self.assertEqual(req['path'], '/projects/%s/subscriptions' % PROJECT)
+        self.assertEqual(req['query_params'], {})
+
     def test_list_subscriptions_with_topic_name(self):
         from gcloud.pubsub.subscription import Subscription
         PROJECT = 'PROJECT'
diff --git a/system_tests/pubsub.py b/system_tests/pubsub.py
--- a/system_tests/pubsub.py
+++ b/system_tests/pubsub.py
@@ -118,6 +118,9 @@ def test_list_subscriptions(self):
         self.assertFalse(topic.exists())
         topic.create()
         self.to_delete.append(topic)
+        empty, _ = Config.CLIENT.list_subscriptions(
+            topic_name=DEFAULT_TOPIC_NAME)
+        self.assertEqual(len(empty), 0)
         subscriptions_to_create = [
             'new%d' % (1000 * time.time(),),
             'newer%d' % (1000 * time.time(),),