Repository: googleapis/google-cloud-python

Issue:
AttributeError: 'str' object has no attribute '_credentials'  
Hi, I'm trying the example code and it's failing with the following exception:

```
>>> c = error_reporting.Client(project='<my-project-str>')
>>> try:
...   raise NameError
... except Exception:
...   c.report_exception()
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "/opt/python/3.6.0/lib/python3.6/site-packages/google/cloud/error_reporting/client.py", line 348, in report_exception
    user=user)
  File "/opt/python/3.6.0/lib/python3.6/site-packages/google/cloud/error_reporting/client.py", line 277, in _send_error_report
    self.report_errors_api.report_error_event(error_report)
  File "/opt/python/3.6.0/lib/python3.6/site-packages/google/cloud/error_reporting/client.py", line 162, in report_errors_api
    self._report_errors_api = make_report_error_api(self._project)
  File "/opt/python/3.6.0/lib/python3.6/site-packages/google/cloud/error_reporting/_gax.py", line 39, in make_report_error_api
    client._credentials,
AttributeError: 'str' object has no attribute '_credentials'
```

Reading the source, this seems like a rather obvious error:

https://github.com/GoogleCloudPlatform/google-cloud-python/blob/master/error_reporting/google/cloud/error_reporting/client.py#L162

`self._project` is a string and at https://github.com/GoogleCloudPlatform/google-cloud-python/blob/master/error_reporting/google/cloud/error_reporting/_gax.py#L29 it's treated like a client object. Is this not the case?


Discussion:
I would agree, it **does** seem like a rather obvious error. Thanks for reporting! 

In the meantime, you can avoid this by setting `GOOGLE_CLOUD_DISABLE_GRPC=True`, but we'll get a fix out.
/cc @waprin @jonparrott 

Resolving patch:
diff --git a/error_reporting/google/cloud/error_reporting/client.py b/error_reporting/google/cloud/error_reporting/client.py
--- a/error_reporting/google/cloud/error_reporting/client.py
+++ b/error_reporting/google/cloud/error_reporting/client.py
@@ -159,7 +159,7 @@ def report_errors_api(self):
         """
         if self._report_errors_api is None:
             if self._use_gax:
-                self._report_errors_api = make_report_error_api(self._project)
+                self._report_errors_api = make_report_error_api(self)
             else:
                 self._report_errors_api = _ErrorReportingLoggingAPI(
                     self._project, self._credentials, self._http)


Test patch:
diff --git a/error_reporting/unit_tests/test_client.py b/error_reporting/unit_tests/test_client.py
--- a/error_reporting/unit_tests/test_client.py
+++ b/error_reporting/unit_tests/test_client.py
@@ -26,48 +26,46 @@ def _make_credentials():
 
 class TestClient(unittest.TestCase):
 
+    PROJECT = 'PROJECT'
+    SERVICE = 'SERVICE'
+    VERSION = 'myversion'
+
     @staticmethod
     def _get_target_class():
         from google.cloud.error_reporting.client import Client
 
         return Client
 
-    def _getHttpContext(self):
-        from google.cloud.error_reporting.client import HTTPContext
-
-        return HTTPContext
-
     def _make_one(self, *args, **kw):
         return self._get_target_class()(*args, **kw)
 
-    def _makeHTTP(self, *args, **kw):
-        return self._getHttpContext()(*args, **kw)
+    def _make_http(self, *args, **kw):
+        from google.cloud.error_reporting.client import HTTPContext
 
-    PROJECT = 'PROJECT'
-    SERVICE = 'SERVICE'
-    VERSION = 'myversion'
+        return HTTPContext(*args, **kw)
 
     @mock.patch(
         'google.cloud.error_reporting.client._determine_default_project')
-    def test_ctor_default(self, _):
-        CREDENTIALS = _make_credentials()
-        target = self._make_one(credentials=CREDENTIALS)
-        self.assertEqual(target.service, target.DEFAULT_SERVICE)
-        self.assertEqual(target.version, None)
+    def test_ctor_default(self, default_mock):
+        credentials = _make_credentials()
+        client = self._make_one(credentials=credentials)
+        self.assertEqual(client.service, client.DEFAULT_SERVICE)
+        self.assertEqual(client.version, None)
+        default_mock.assert_called_once_with()
 
     def test_ctor_params(self):
-        CREDENTIALS = _make_credentials()
-        target = self._make_one(project=self.PROJECT,
-                                credentials=CREDENTIALS,
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT,
+                                credentials=credentials,
                                 service=self.SERVICE,
                                 version=self.VERSION)
-        self.assertEqual(target.service, self.SERVICE)
-        self.assertEqual(target.version, self.VERSION)
+        self.assertEqual(client.service, self.SERVICE)
+        self.assertEqual(client.version, self.VERSION)
 
     def test_report_exception_with_gax(self):
-        CREDENTIALS = _make_credentials()
-        target = self._make_one(project=self.PROJECT,
-                                credentials=CREDENTIALS)
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT,
+                                credentials=credentials)
 
         patch = mock.patch(
             'google.cloud.error_reporting.client.make_report_error_api')
@@ -75,18 +73,20 @@ def test_report_exception_with_gax(self):
             try:
                 raise NameError
             except NameError:
-                target.report_exception()
+                client.report_exception()
             payload = make_api.return_value.report_error_event.call_args[0][0]
+            make_api.assert_called_once_with(client)
+
         self.assertEqual(payload['serviceContext'], {
-            'service': target.DEFAULT_SERVICE,
+            'service': client.DEFAULT_SERVICE,
         })
         self.assertIn('test_report', payload['message'])
         self.assertIn('test_client.py', payload['message'])
 
     def test_report_exception_wo_gax(self):
-        CREDENTIALS = _make_credentials()
-        target = self._make_one(project=self.PROJECT,
-                                credentials=CREDENTIALS,
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT,
+                                credentials=credentials,
                                 use_gax=False)
         patch = mock.patch(
             'google.cloud.error_reporting.client._ErrorReportingLoggingAPI'
@@ -95,43 +95,46 @@ def test_report_exception_wo_gax(self):
             try:
                 raise NameError
             except NameError:
-                target.report_exception()
+                client.report_exception()
             mock_report = _error_api.return_value.report_error_event
             payload = mock_report.call_args[0][0]
 
         self.assertEqual(payload['serviceContext'], {
-            'service': target.DEFAULT_SERVICE,
+            'service': client.DEFAULT_SERVICE,
         })
         self.assertIn('test_report', payload['message'])
         self.assertIn('test_client.py', payload['message'])
-        self.assertIsNotNone(target.report_errors_api)
+        self.assertIsNotNone(client.report_errors_api)
 
     @mock.patch('google.cloud.error_reporting.client.make_report_error_api')
     def test_report_exception_with_service_version_in_constructor(
-            self, make_client):
-        CREDENTIALS = _make_credentials()
-        SERVICE = "notdefault"
-        VERSION = "notdefaultversion"
-        target = self._make_one(project=self.PROJECT,
-                                credentials=CREDENTIALS,
-                                service=SERVICE,
-                                version=VERSION)
-
-        http_context = self._makeHTTP(method="GET", response_status_code=500)
-        USER = "user@gmail.com"
-
-        client = mock.Mock()
-        make_client.return_value = client
+            self, make_api):
+        credentials = _make_credentials()
+        service = 'notdefault'
+        version = 'notdefaultversion'
+        client = self._make_one(project=self.PROJECT,
+                                credentials=credentials,
+                                service=service,
+                                version=version)
+
+        http_context = self._make_http(
+            method='GET', response_status_code=500)
+        user = 'user@gmail.com'
+
+        error_api = mock.Mock(spec=['report_error_event'])
+        make_api.return_value = error_api
 
         try:
             raise NameError
         except NameError:
-            target.report_exception(http_context=http_context, user=USER)
+            client.report_exception(http_context=http_context, user=user)
+
+        make_api.assert_called_once_with(client)
 
-        payload = client.report_error_event.call_args[0][0]
+        payload = error_api.report_error_event.call_args[0][0]
         self.assertEqual(payload['serviceContext'], {
-            'service': SERVICE,
-            'version': VERSION
+            'service': service,
+            'version': version
         })
         self.assertIn(
             'test_report_exception_with_service_version_in_constructor',
@@ -141,23 +144,24 @@ def test_report_exception_with_service_version_in_constructor(
             payload['context']['httpContext']['responseStatusCode'], 500)
         self.assertEqual(
             payload['context']['httpContext']['method'], 'GET')
-        self.assertEqual(payload['context']['user'], USER)
+        self.assertEqual(payload['context']['user'], user)
 
     @mock.patch('google.cloud.error_reporting.client.make_report_error_api')
-    def test_report(self, make_client):
-        CREDENTIALS = _make_credentials()
-        target = self._make_one(project=self.PROJECT,
-                                credentials=CREDENTIALS)
+    def test_report(self, make_api):
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT,
+                                credentials=credentials)
 
-        client = mock.Mock()
-        make_client.return_value = client
+        error_api = mock.Mock(spec=['report_error_event'])
+        make_api.return_value = error_api
 
-        MESSAGE = 'this is an error'
-        target.report(MESSAGE)
+        message = 'this is an error'
+        client.report(message)
 
-        payload = client.report_error_event.call_args[0][0]
+        payload = error_api.report_error_event.call_args[0][0]
+        make_api.assert_called_once_with(client)
 
-        self.assertEqual(payload['message'], MESSAGE)
+        self.assertEqual(payload['message'], message)
         report_location = payload['context']['reportLocation']
         self.assertIn('test_client.py', report_location['filePath'])
         self.assertEqual(report_location['functionName'], 'test_report')