Repository: googleapis/google-cloud-python

Issue:
Pub/Sub MergeFrom error publish message
🏷 pub/sub
SO: Linux/Ubuntu 16.04
api_core_version = 1.3.0
client_library_version = None
gapic_version = 0.37.2
grpc_version = 1.14.2
python_version = 3.6.4

```
client = pubsub.PublisherClient.from_service_account_json('account.json')
topic_path = client.topic_path('project_name', 'topic_name')
data = b'The rain in Wales falls mainly on the snails.'
response = client.publish(topic, data)
```

Version: google-cloud-pubsub==0.37.2

## Error:
` response = client.publish(topic_path, data)
  File "venvpy/lib/python3.6/site-packages/google/cloud/pubsub_v1/gapic/publisher_client.py", line 393, in publish
    messages=messages,
TypeError: Parameter to MergeFrom() must be instance of same class: expected google.pubsub.v1.PubsubMessage got int.`




Discussion:
Seems to be a problem on the pubsub.PublisherClient.from_service_account_json.
I try a different approach using the credentials and works fine!

```
from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file('account.json')
client = pubsub.PublisherClient(credentials=credentials)
```

Sorry, but now I don't have a lot of time to debug the "why".
I don't think there can be any connection between the `Client.from_service_account_json` factory and the traceback you are reporting.  What version of `google-cloud-pubsub` do you have installed?  E.g.:

```bash
$ pip show google-cloud-pubsub
```
Version: google-cloud-pubsub==0.37.2
I just got the same error, `google-cloud-pubsub==0.38.0`.  And the same workaround worked for me too (thanks Paulo).  [Possibly related StackOverflow question](https://stackoverflow.com/questions/31843088/isinstance-doesnt-work-when-use-reflection-of-python-protobuf-v2-6-1) ?
@paulorcf It looks as though you are bypassing the "main" API for publishing messages ([`pubsub_v1.PublisherClient.publish`](https://googlecloudplatform.github.io/google-cloud-python/latest/pubsub/publisher/api/client.html#google.cloud.pubsub_v1.publisher.client.Client.publish)), and instead calling the lower-level, GAPIC-generated helper (`pubsub_v1.gapic.publisher_client.PublisherClient.publish`]), which is definitely not recommended.

 The error message you report indicates that one of the items you are passing in `data` is an int, rather than an instance of `google.pubsub.v1.PubsubMessage`.    Can you show the code in your application which triggers the error?

The recommended path would be:

```python
from google.cloud import pubsub_v1

client = pubsub_v1.PublisherClient()
topic_path = client.topic_path('my-project-123'. 'my-topic-name')
data = b'BYTES OF MESSAGE DATA'

client.publish(topic_path, data, attr='ATTR VALUE')
```

Please feel free to reopen / follow up if you can show the reproduction case where your app passes values using this pattern.
I believe they showed their code in the original post, re-pasting it here, it's the same I used.  It's the `from_service_account_json` constructor. 

```python
client = pubsub.PublisherClient.from_service_account_json('account.json')
topic_path = client.topic_path('project_name', 'topic_name')
data = b'The rain in Wales falls mainly on the snails.'
response = client.publish(topic, data)
```

@ericgj we have [passing system test for `PublisherClient.publish`](https://github.com/GoogleCloudPlatform/google-cloud-python/blob/db2019aab45775ca8955cbc67b5df1c2be5870b8/pubsub/tests/system.py#L69).

Can you provide the complete traceback, along with the value you are passing to `publish`?
Working:
```
from google.cloud import pubsub_v1
from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file('auth.json')
client = pubsub_v1.PublisherClient(credentials=credentials)
topic = client.topic_path('project, 'topic')
client.publish(topic, b'hello')

```

Not Working:
```
from google.cloud import pubsub_v1

client = pubsub_v1.PublisherClient.from_service_account_json('auth.json')
topic = client.topic_path('project, 'topic')
client.publish(topic, b'hello')
```

TRACEBACK:
```
...
  File ".../python3.6/site-packages/google/cloud/pubsub_v1/gapic/publisher_client.py", line 395, in publish
    messages=messages,
TypeError: Parameter to MergeFrom() must be instance of same class: expected google.pubsub.v1.PubsubMessage got int.
```


OK, I can reproduce here.  The issue is that `pubsub_v1.PublisherClient.from_service_account` is returning an instance of the low-level `pubsub_v1.gapic.publisher_client.PublisherClient`:  that class has a different signature for `publish`.
@theacodes I believe this is the issue you tried to fix in #5826.  Can you shed any light here?
Looks like #5826 returns a `staticmethod`, rather than a `classmethod`, for things that started life as `classmethod`s.  That would explain seeing the wrong `cls`.

Resolving patch:
diff --git a/pubsub/google/cloud/pubsub_v1/publisher/client.py b/pubsub/google/cloud/pubsub_v1/publisher/client.py
--- a/pubsub/google/cloud/pubsub_v1/publisher/client.py
+++ b/pubsub/google/cloud/pubsub_v1/publisher/client.py
@@ -22,6 +22,7 @@
 import six
 
 from google.api_core import grpc_helpers
+from google.oauth2 import service_account
 
 from google.cloud.pubsub_v1 import _gapic
 from google.cloud.pubsub_v1 import types
@@ -31,8 +32,15 @@
 
 __version__ = pkg_resources.get_distribution('google-cloud-pubsub').version
 
+_BLACKLISTED_METHODS = (
+    'publish',
+    'from_service_account_file',
+    'from_service_account_json',
+)
 
-@_gapic.add_methods(publisher_client.PublisherClient, blacklist=('publish',))
+
+@_gapic.add_methods(
+    publisher_client.PublisherClient, blacklist=_BLACKLISTED_METHODS)
 class Client(object):
     """A publisher client for Google Cloud Pub/Sub.
 
@@ -86,6 +94,28 @@ def __init__(self, batch_settings=(), **kwargs):
         self._batch_lock = self._batch_class.make_lock()
         self._batches = {}
 
+    @classmethod
+    def from_service_account_file(cls, filename, batch_settings=(), **kwargs):
+        """Creates an instance of this client using the provided credentials
+        file.
+
+        Args:
+            filename (str): The path to the service account private key json
+                file.
+            batch_settings (~google.cloud.pubsub_v1.types.BatchSettings): The
+                settings for batch publishing.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            PublisherClient: The constructed client.
+        """
+        credentials = service_account.Credentials.from_service_account_file(
+            filename)
+        kwargs['credentials'] = credentials
+        return cls(batch_settings, **kwargs)
+
+    from_service_account_json = from_service_account_file
+
     @property
     def target(self):
         """Return the target (where the API is).
diff --git a/pubsub/google/cloud/pubsub_v1/subscriber/client.py b/pubsub/google/cloud/pubsub_v1/subscriber/client.py
--- a/pubsub/google/cloud/pubsub_v1/subscriber/client.py
+++ b/pubsub/google/cloud/pubsub_v1/subscriber/client.py
@@ -20,6 +20,7 @@
 import grpc
 
 from google.api_core import grpc_helpers
+from google.oauth2 import service_account
 
 from google.cloud.pubsub_v1 import _gapic
 from google.cloud.pubsub_v1 import types
@@ -30,9 +31,15 @@
 
 __version__ = pkg_resources.get_distribution('google-cloud-pubsub').version
 
+_BLACKLISTED_METHODS = (
+    'publish',
+    'from_service_account_file',
+    'from_service_account_json',
+)
 
-@_gapic.add_methods(subscriber_client.SubscriberClient,
-                    blacklist=('streaming_pull',))
+
+@_gapic.add_methods(
+    subscriber_client.SubscriberClient, blacklist=_BLACKLISTED_METHODS)
 class Client(object):
     """A subscriber client for Google Cloud Pub/Sub.
 
@@ -75,6 +82,26 @@ def __init__(self, **kwargs):
         # client.
         self._api = subscriber_client.SubscriberClient(**kwargs)
 
+    @classmethod
+    def from_service_account_file(cls, filename, **kwargs):
+        """Creates an instance of this client using the provided credentials
+        file.
+
+        Args:
+            filename (str): The path to the service account private key json
+                file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            PublisherClient: The constructed client.
+        """
+        credentials = service_account.Credentials.from_service_account_file(
+            filename)
+        kwargs['credentials'] = credentials
+        return cls(**kwargs)
+
+    from_service_account_json = from_service_account_file
+
     @property
     def target(self):
         """Return the target (where the API is).


Test patch:
diff --git a/pubsub/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/pubsub/tests/unit/pubsub_v1/publisher/test_publisher_client.py
--- a/pubsub/tests/unit/pubsub_v1/publisher/test_publisher_client.py
+++ b/pubsub/tests/unit/pubsub_v1/publisher/test_publisher_client.py
@@ -221,6 +221,16 @@ def test_gapic_class_method_on_class():
     assert answer == 'projects/foo/topics/bar'
 
 
+def test_class_method_factory():
+    patch = mock.patch(
+        'google.oauth2.service_account.Credentials.from_service_account_file')
+
+    with patch:
+        client = publisher.Client.from_service_account_file('filename.json')
+
+    assert isinstance(client, publisher.Client)
+
+
 def test_gapic_class_method_on_instance():
     creds = mock.Mock(spec=credentials.Credentials)
     client = publisher.Client(credentials=creds)
diff --git a/pubsub/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/pubsub/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py
--- a/pubsub/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py
+++ b/pubsub/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py
@@ -40,6 +40,16 @@ def test_init_emulator(monkeypatch):
     assert channel.target().decode('utf8') == '/baz/bacon/'
 
 
+def test_class_method_factory():
+    patch = mock.patch(
+        'google.oauth2.service_account.Credentials.from_service_account_file')
+
+    with patch:
+        client = subscriber.Client.from_service_account_file('filename.json')
+
+    assert isinstance(client, subscriber.Client)
+
+
 @mock.patch(
     'google.cloud.pubsub_v1.subscriber._protocol.streaming_pull_manager.'
     'StreamingPullManager.open', autospec=True)