Repository: googleapis/google-cloud-python

Issue:
Return integer or None from 'Query.total_rows'
Split out from #3001, where @amirshavit reported:

> It would be 'nicer' if the `total_rows property` return an integer (or None) rather than a string.


Discussion:

Resolving patch:
diff --git a/bigquery/google/cloud/bigquery/query.py b/bigquery/google/cloud/bigquery/query.py
--- a/bigquery/google/cloud/bigquery/query.py
+++ b/bigquery/google/cloud/bigquery/query.py
@@ -208,7 +208,9 @@ def total_rows(self):
         :rtype: int, or ``NoneType``
         :returns: Count generated on the server (None until set by the server).
         """
-        return self._properties.get('totalRows')
+        total_rows = self._properties.get('totalRows')
+        if total_rows is not None:
+            return int(total_rows)
 
     @property
     def total_bytes_processed(self):
@@ -220,7 +222,9 @@ def total_bytes_processed(self):
         :rtype: int, or ``NoneType``
         :returns: Count generated on the server (None until set by the server).
         """
-        return self._properties.get('totalBytesProcessed')
+        total_bytes_processed = self._properties.get('totalBytesProcessed')
+        if total_bytes_processed is not None:
+            return int(total_bytes_processed)
 
     @property
     def rows(self):


Test patch:
diff --git a/bigquery/unit_tests/test_query.py b/bigquery/unit_tests/test_query.py
--- a/bigquery/unit_tests/test_query.py
+++ b/bigquery/unit_tests/test_query.py
@@ -124,9 +124,15 @@ def _verifyResourceProperties(self, query, resource):
         self.assertEqual(query.complete, resource.get('jobComplete'))
         self.assertEqual(query.errors, resource.get('errors'))
         self.assertEqual(query.page_token, resource.get('pageToken'))
-        self.assertEqual(query.total_rows, resource.get('totalRows'))
-        self.assertEqual(query.total_bytes_processed,
-                         resource.get('totalBytesProcessed'))
+        if 'totalRows' in resource:
+            self.assertEqual(query.total_rows, int(resource['totalRows']))
+        else:
+            self.assertIsNone(query.total_rows)
+        if 'totalBytesProcessed' in resource:
+            self.assertEqual(query.total_bytes_processed,
+                             int(resource['totalBytesProcessed']))
+        else:
+            self.assertIsNone(query.total_bytes_processed)
 
         if 'jobReference' in resource:
             self.assertEqual(query.name, resource['jobReference']['jobId'])
@@ -251,6 +257,120 @@ def test_job_w_jobid(self):
         fetched_later = query.job
         self.assertIs(fetched_later, job)
 
+    def test_cache_hit_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.cache_hit)
+
+    def test_cache_hit_present(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'cacheHit': True}
+        query._set_properties(resource)
+        self.assertTrue(query.cache_hit)
+
+    def test_complete_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.complete)
+
+    def test_complete_present(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'jobComplete': True}
+        query._set_properties(resource)
+        self.assertTrue(query.complete)
+
+    def test_errors_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.errors)
+
+    def test_errors_present(self):
+        ERRORS = [
+            {'reason': 'testing'},
+        ]
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'errors': ERRORS}
+        query._set_properties(resource)
+        self.assertEqual(query.errors, ERRORS)
+
+    def test_name_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.name)
+
+    def test_name_broken_job_reference(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'jobReference': {'bogus': 'BOGUS'}}
+        query._set_properties(resource)
+        self.assertIsNone(query.name)
+
+    def test_name_present(self):
+        JOB_ID = 'JOB_ID'
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'jobReference': {'jobId': JOB_ID}}
+        query._set_properties(resource)
+        self.assertEqual(query.name, JOB_ID)
+
+    def test_page_token_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.page_token)
+
+    def test_page_token_present(self):
+        TOKEN = 'TOKEN'
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'pageToken': TOKEN}
+        query._set_properties(resource)
+        self.assertEqual(query.page_token, TOKEN)
+
+    def test_total_rows_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.total_rows)
+
+    def test_total_rows_present_integer(self):
+        TOTAL_ROWS = 42
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'totalRows': TOTAL_ROWS}
+        query._set_properties(resource)
+        self.assertEqual(query.total_rows, TOTAL_ROWS)
+
+    def test_total_rows_present_string(self):
+        TOTAL_ROWS = 42
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'totalRows': str(TOTAL_ROWS)}
+        query._set_properties(resource)
+        self.assertEqual(query.total_rows, TOTAL_ROWS)
+
+    def test_total_bytes_processed_missing(self):
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        self.assertIsNone(query.total_bytes_processed)
+
+    def test_total_bytes_processed_present_integer(self):
+        TOTAL_BYTES_PROCESSED = 123456
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'totalBytesProcessed': TOTAL_BYTES_PROCESSED}
+        query._set_properties(resource)
+        self.assertEqual(query.total_bytes_processed, TOTAL_BYTES_PROCESSED)
+
+    def test_total_bytes_processed_present_string(self):
+        TOTAL_BYTES_PROCESSED = 123456
+        client = _Client(self.PROJECT)
+        query = self._make_one(self.QUERY, client)
+        resource = {'totalBytesProcessed': str(TOTAL_BYTES_PROCESSED)}
+        query._set_properties(resource)
+        self.assertEqual(query.total_bytes_processed, TOTAL_BYTES_PROCESSED)
+
     def test_schema(self):
         client = _Client(self.PROJECT)
         query = self._make_one(self.QUERY, client)