Repository: googleapis/python-bigquery

Issue:
TimePartitioning printable representation is not evaluable
#### Environment details

  - OS type and version: `All`
  - Python version: `All`
  - pip version: `All`
  - google-cloud-bigquery version: `1.24.0`

#### Code example

```python
from google.cloud.bigquery import *
eval(TimePartitioning(field='myfield').__repr__())
```

#### Stack trace
```
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-2705370d2a8c> in <module>
      1 from google.cloud.bigquery import *
----> 2 eval(TimePartitioning(field='myfield').__repr__())
<string> in <module>
NameError: name 'myfield' is not defined
```

#### Code example

```python
from google.cloud.bigquery import *
eval(TimePartitioning(field='myfield',type='DAY').__repr__())
```

#### Stack trace
```

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-4733c3100f2b> in <module>
      1 from google.cloud.bigquery import *
----> 2 eval(TimePartitioning(field='myfield',type='DAY').__repr__())
TypeError: __init__() got an unexpected keyword argument 'type'
```


Discussion:

Resolving patch:
diff --git a/google/cloud/bigquery/table.py b/google/cloud/bigquery/table.py
--- a/google/cloud/bigquery/table.py
+++ b/google/cloud/bigquery/table.py
@@ -2114,7 +2114,20 @@ def to_api_repr(self):
         return self._properties
 
     def _key(self):
-        return tuple(sorted(self._properties.items()))
+        # because we are only "renaming" top level keys shallow copy is sufficient here.
+        properties = self._properties.copy()
+        # calling repr for non built-in type objects.
+        properties["type_"] = repr(properties.pop("type"))
+        if "field" in properties:
+            # calling repr for non built-in type objects.
+            properties["field"] = repr(properties["field"])
+        if "requirePartitionFilter" in properties:
+            properties["require_partition_filter"] = properties.pop(
+                "requirePartitionFilter"
+            )
+        if "expirationMs" in properties:
+            properties["expiration_ms"] = properties.pop("expirationMs")
+        return tuple(sorted(properties.items()))
 
     def __eq__(self, other):
         if not isinstance(other, TimePartitioning):


Test patch:
diff --git a/tests/unit/test_table.py b/tests/unit/test_table.py
--- a/tests/unit/test_table.py
+++ b/tests/unit/test_table.py
@@ -3711,7 +3711,7 @@ def test___hash__not_equals(self):
 
     def test___repr___minimal(self):
         time_partitioning = self._make_one()
-        expected = "TimePartitioning(type=DAY)"
+        expected = "TimePartitioning(type_='DAY')"
         self.assertEqual(repr(time_partitioning), expected)
 
     def test___repr___explicit(self):
@@ -3720,7 +3720,7 @@ def test___repr___explicit(self):
         time_partitioning = self._make_one(
             type_=TimePartitioningType.DAY, field="name", expiration_ms=10000
         )
-        expected = "TimePartitioning(" "expirationMs=10000," "field=name," "type=DAY)"
+        expected = "TimePartitioning(expiration_ms=10000,field='name',type_='DAY')"
         self.assertEqual(repr(time_partitioning), expected)
 
     def test_set_expiration_w_none(self):