Repository: googleapis/google-cloud-python

Issue:
PubSub - Error message on subscription.close()
Python version: 2.7.14
API Client version: 0.33.0

While closing a subscription, the following message is printed to stderr but no exception is thrown:

```
ERROR:root:Exception serializing message!
Traceback (most recent call last):
  File "/miniconda/lib/python2.7/site-packages/grpc/_common.py", line 87, in _transform
    return transformer(message)
TypeError: descriptor 'SerializeToString' requires a 'google.protobuf.pyext._message.CMessage' object but received a 'UUID'
```

Recreated by creating a topic+subscription and running the following script:

```
import google.cloud.pubsub

sub_client = google.cloud.pubsub.SubscriberClient()
sub_name = sub_client.subscription_path('[PROJECT]', '[SUBSCRIPTION]')

sub = sub_client.subscribe(sub_name)
sub.open(lambda x: x.ack())
sub.close()
```


Discussion:
Hrm, I haven't been able to reproduce this. Does this happen consistently for you?
@jonparrott Yes, both with a PubSub emulator and non-emulator, but only with version 0.33.0. When we roll back to 0.32.1 the problem disappears.
Ah okay, I got it reproduced. Will investigate.

Resolving patch:
diff --git a/pubsub/google/cloud/pubsub_v1/subscriber/_consumer.py b/pubsub/google/cloud/pubsub_v1/subscriber/_consumer.py
--- a/pubsub/google/cloud/pubsub_v1/subscriber/_consumer.py
+++ b/pubsub/google/cloud/pubsub_v1/subscriber/_consumer.py
@@ -210,6 +210,12 @@ def __iter__(self):
                     # RPC is still active, keep waiting for queue items.
                     continue
 
+            # A call to consumer.close() signaled us to stop generating
+            # requests.
+            if item == _helper_threads.STOP:
+                _LOGGER.debug('Cleanly exiting request generator.')
+                return
+
             if self._should_exit():
                 # We have an item, but the RPC is closed. We should put the
                 # item back on the queue so that the next RPC can consume it.
@@ -416,6 +422,7 @@ def _stop_no_join(self):
         self.resume()  # Make sure we aren't paused.
         self._stopped.set()
         _LOGGER.debug('Stopping helper thread %s', self._consumer_thread.name)
+        # Signal the request generator RPC to exit cleanly.
         self.send_request(_helper_threads.STOP)
         thread = self._consumer_thread
         self._consumer_thread = None


Test patch:
diff --git a/pubsub/tests/unit/pubsub_v1/subscriber/test_consumer.py b/pubsub/tests/unit/pubsub_v1/subscriber/test_consumer.py
--- a/pubsub/tests/unit/pubsub_v1/subscriber/test_consumer.py
+++ b/pubsub/tests/unit/pubsub_v1/subscriber/test_consumer.py
@@ -17,7 +17,6 @@
 from google.auth import credentials
 import grpc
 import mock
-import pytest
 from six.moves import queue
 
 from google.cloud.pubsub_v1 import subscriber
@@ -92,6 +91,19 @@ def test_exit_when_inactive_empty(self):
 
         assert items == []
 
+    def test_exit_with_stop(self):
+        q = mock.create_autospec(queue.Queue, instance=True)
+        q.get.side_effect = [_helper_threads.STOP, queue.Empty()]
+        rpc = mock.create_autospec(grpc.RpcContext, instance=True)
+        rpc.is_active.return_value = True
+
+        generator = _consumer._RequestQueueGenerator(q)
+        generator.rpc = rpc
+
+        items = list(generator)
+
+        assert items == []
+
 
 def test_send_request():
     consumer = _consumer.Consumer()