Skip to content

Commit ce9a06c

Browse files
committed
BigQuery: add to/from API representation for Table & Dataset references. (#4020)
* BigQuery: add to/from API representation for Table & Dataset references. Also, implement equality and hashing for Table & Dataset references. This will make it easier to use the TableReference and DatasetReference classes as typed properties in the QueryJob and other job classes. * Fix lint errors. * Replace unique-ly with uniquely.
1 parent f6d7582 commit ce9a06c

File tree

5 files changed

+253
-1
lines changed

5 files changed

+253
-1
lines changed

bigquery/google/cloud/bigquery/dataset.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,45 @@ def table(self, table_id):
151151
"""
152152
return TableReference(self, table_id)
153153

154+
@classmethod
155+
def from_api_repr(cls, resource):
156+
project = resource['projectId']
157+
dataset_id = resource['datasetId']
158+
return cls(project, dataset_id)
159+
160+
def to_api_repr(self):
161+
return {
162+
'projectId': self._project,
163+
'datasetId': self._dataset_id,
164+
}
165+
166+
def _key(self):
167+
"""A tuple key that uniquely describes this field.
168+
169+
Used to compute this instance's hashcode and evaluate equality.
170+
171+
Returns:
172+
tuple: The contents of this :class:`DatasetReference`.
173+
"""
174+
return (
175+
self._project,
176+
self._dataset_id,
177+
)
178+
179+
def __eq__(self, other):
180+
if not isinstance(other, DatasetReference):
181+
return NotImplemented
182+
return self._key() == other._key()
183+
184+
def __ne__(self, other):
185+
return not self == other
186+
187+
def __hash__(self):
188+
return hash(self._key())
189+
190+
def __repr__(self):
191+
return 'DatasetReference{}'.format(self._key())
192+
154193

155194
class Dataset(object):
156195
"""Datasets are containers for tables.

bigquery/google/cloud/bigquery/schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def to_api_repr(self):
126126
return answer
127127

128128
def _key(self):
129-
"""A tuple key that unique-ly describes this field.
129+
"""A tuple key that uniquely describes this field.
130130
131131
Used to compute this instance's hashcode and evaluate equality.
132132

bigquery/google/cloud/bigquery/table.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,63 @@ def path(self):
105105
return '/projects/%s/datasets/%s/tables/%s' % (
106106
self._project, self._dataset_id, self._table_id)
107107

108+
@classmethod
109+
def from_api_repr(cls, resource):
110+
"""Factory: construct a table reference given its API representation
111+
112+
:type resource: dict
113+
:param resource: table reference representation returned from the API
114+
115+
:rtype: :class:`google.cloud.bigquery.table.TableReference`
116+
:returns: Table reference parsed from ``resource``.
117+
"""
118+
from google.cloud.bigquery.dataset import DatasetReference
119+
120+
project = resource['projectId']
121+
dataset_id = resource['datasetId']
122+
table_id = resource['tableId']
123+
return cls(DatasetReference(project, dataset_id), table_id)
124+
125+
def to_api_repr(self):
126+
"""Construct the API resource representation of this table reference.
127+
128+
:rtype: dict
129+
:returns: Table reference as represented as an API resource
130+
"""
131+
return {
132+
'projectId': self._project,
133+
'datasetId': self._dataset_id,
134+
'tableId': self._table_id,
135+
}
136+
137+
def _key(self):
138+
"""A tuple key that uniquely describes this field.
139+
140+
Used to compute this instance's hashcode and evaluate equality.
141+
142+
Returns:
143+
tuple: The contents of this :class:`DatasetReference`.
144+
"""
145+
return (
146+
self._project,
147+
self._dataset_id,
148+
self._table_id,
149+
)
150+
151+
def __eq__(self, other):
152+
if not isinstance(other, TableReference):
153+
return NotImplemented
154+
return self._key() == other._key()
155+
156+
def __ne__(self, other):
157+
return not self == other
158+
159+
def __hash__(self):
160+
return hash(self._key())
161+
162+
def __repr__(self):
163+
return 'TableReference{}'.format(self._key())
164+
108165

109166
class Table(object):
110167
"""Tables represent a set of rows whose values correspond to a schema.

bigquery/tests/unit/test_dataset.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,70 @@ def test_table(self):
114114
self.assertEqual(table_ref.project, 'some-project-1')
115115
self.assertEqual(table_ref.table_id, 'table_1')
116116

117+
def test_to_api_repr(self):
118+
dataset = self._make_one('project_1', 'dataset_1')
119+
120+
resource = dataset.to_api_repr()
121+
122+
self.assertEqual(
123+
resource,
124+
{
125+
'projectId': 'project_1',
126+
'datasetId': 'dataset_1',
127+
})
128+
129+
def test_from_api_repr(self):
130+
from google.cloud.bigquery.dataset import DatasetReference
131+
expected = self._make_one('project_1', 'dataset_1')
132+
133+
got = DatasetReference.from_api_repr(
134+
{
135+
'projectId': 'project_1',
136+
'datasetId': 'dataset_1',
137+
})
138+
139+
self.assertEqual(expected, got)
140+
141+
def test___eq___wrong_type(self):
142+
dataset = self._make_one('project_1', 'dataset_1')
143+
other = object()
144+
self.assertNotEqual(dataset, other)
145+
self.assertEqual(dataset, mock.ANY)
146+
147+
def test___eq___project_mismatch(self):
148+
dataset = self._make_one('project_1', 'dataset_1')
149+
other = self._make_one('project_2', 'dataset_1')
150+
self.assertNotEqual(dataset, other)
151+
152+
def test___eq___dataset_mismatch(self):
153+
dataset = self._make_one('project_1', 'dataset_1')
154+
other = self._make_one('project_1', 'dataset_2')
155+
self.assertNotEqual(dataset, other)
156+
157+
def test___eq___equality(self):
158+
dataset = self._make_one('project_1', 'dataset_1')
159+
other = self._make_one('project_1', 'dataset_1')
160+
self.assertEqual(dataset, other)
161+
162+
def test___hash__set_equality(self):
163+
dataset1 = self._make_one('project_1', 'dataset_1')
164+
dataset2 = self._make_one('project_1', 'dataset_2')
165+
set_one = {dataset1, dataset2}
166+
set_two = {dataset1, dataset2}
167+
self.assertEqual(set_one, set_two)
168+
169+
def test___hash__not_equals(self):
170+
dataset1 = self._make_one('project_1', 'dataset_1')
171+
dataset2 = self._make_one('project_1', 'dataset_2')
172+
set_one = {dataset1}
173+
set_two = {dataset2}
174+
self.assertNotEqual(set_one, set_two)
175+
176+
def test___repr__(self):
177+
dataset = self._make_one('project1', 'dataset1')
178+
expected = "DatasetReference('project1', 'dataset1')"
179+
self.assertEqual(repr(dataset), expected)
180+
117181

118182
class TestDataset(unittest.TestCase):
119183
from google.cloud.bigquery.dataset import DatasetReference

bigquery/tests/unit/test_table.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,98 @@ def test_ctor_defaults(self):
5858
self.assertEqual(table_ref.dataset_id, dataset_ref.dataset_id)
5959
self.assertEqual(table_ref.table_id, 'table_1')
6060

61+
def test_to_api_repr(self):
62+
from google.cloud.bigquery.dataset import DatasetReference
63+
dataset_ref = DatasetReference('project_1', 'dataset_1')
64+
table_ref = self._make_one(dataset_ref, 'table_1')
65+
66+
resource = table_ref.to_api_repr()
67+
68+
self.assertEqual(
69+
resource,
70+
{
71+
'projectId': 'project_1',
72+
'datasetId': 'dataset_1',
73+
'tableId': 'table_1',
74+
})
75+
76+
def test_from_api_repr(self):
77+
from google.cloud.bigquery.dataset import DatasetReference
78+
from google.cloud.bigquery.table import TableReference
79+
dataset_ref = DatasetReference('project_1', 'dataset_1')
80+
expected = self._make_one(dataset_ref, 'table_1')
81+
82+
got = TableReference.from_api_repr(
83+
{
84+
'projectId': 'project_1',
85+
'datasetId': 'dataset_1',
86+
'tableId': 'table_1',
87+
})
88+
89+
self.assertEqual(expected, got)
90+
91+
def test___eq___wrong_type(self):
92+
from google.cloud.bigquery.dataset import DatasetReference
93+
dataset_ref = DatasetReference('project_1', 'dataset_1')
94+
table = self._make_one(dataset_ref, 'table_1')
95+
other = object()
96+
self.assertNotEqual(table, other)
97+
self.assertEqual(table, mock.ANY)
98+
99+
def test___eq___project_mismatch(self):
100+
from google.cloud.bigquery.dataset import DatasetReference
101+
dataset = DatasetReference('project_1', 'dataset_1')
102+
other_dataset = DatasetReference('project_2', 'dataset_1')
103+
table = self._make_one(dataset, 'table_1')
104+
other = self._make_one(other_dataset, 'table_1')
105+
self.assertNotEqual(table, other)
106+
107+
def test___eq___dataset_mismatch(self):
108+
from google.cloud.bigquery.dataset import DatasetReference
109+
dataset = DatasetReference('project_1', 'dataset_1')
110+
other_dataset = DatasetReference('project_1', 'dataset_2')
111+
table = self._make_one(dataset, 'table_1')
112+
other = self._make_one(other_dataset, 'table_1')
113+
self.assertNotEqual(table, other)
114+
115+
def test___eq___table_mismatch(self):
116+
from google.cloud.bigquery.dataset import DatasetReference
117+
dataset = DatasetReference('project_1', 'dataset_1')
118+
table = self._make_one(dataset, 'table_1')
119+
other = self._make_one(dataset, 'table_2')
120+
self.assertNotEqual(table, other)
121+
122+
def test___eq___equality(self):
123+
from google.cloud.bigquery.dataset import DatasetReference
124+
dataset = DatasetReference('project_1', 'dataset_1')
125+
table = self._make_one(dataset, 'table_1')
126+
other = self._make_one(dataset, 'table_1')
127+
self.assertEqual(table, other)
128+
129+
def test___hash__set_equality(self):
130+
from google.cloud.bigquery.dataset import DatasetReference
131+
dataset = DatasetReference('project_1', 'dataset_1')
132+
table1 = self._make_one(dataset, 'table1')
133+
table2 = self._make_one(dataset, 'table2')
134+
set_one = {table1, table2}
135+
set_two = {table1, table2}
136+
self.assertEqual(set_one, set_two)
137+
138+
def test___hash__not_equals(self):
139+
from google.cloud.bigquery.dataset import DatasetReference
140+
dataset = DatasetReference('project_1', 'dataset_1')
141+
table1 = self._make_one(dataset, 'table1')
142+
table2 = self._make_one(dataset, 'table2')
143+
set_one = {table1}
144+
set_two = {table2}
145+
self.assertNotEqual(set_one, set_two)
146+
147+
def test___repr__(self):
148+
dataset = DatasetReference('project1', 'dataset1')
149+
table1 = self._make_one(dataset, 'table1')
150+
expected = "TableReference('project1', 'dataset1', 'table1')"
151+
self.assertEqual(repr(table1), expected)
152+
61153

62154
class TestTable(unittest.TestCase, _SchemaBase):
63155

0 commit comments

Comments
 (0)