From eb7cf36317ca3251e110cf80a7ed6f906eba9be6 Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Thu, 21 Sep 2017 13:52:33 -0700 Subject: [PATCH 1/3] 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. --- bigquery/google/cloud/bigquery/dataset.py | 40 ++++++++++ bigquery/google/cloud/bigquery/table.py | 57 ++++++++++++++ bigquery/tests/unit/test_dataset.py | 64 ++++++++++++++++ bigquery/tests/unit/test_table.py | 92 +++++++++++++++++++++++ 4 files changed, 253 insertions(+) diff --git a/bigquery/google/cloud/bigquery/dataset.py b/bigquery/google/cloud/bigquery/dataset.py index fc641c3a4d58..0f945b7fb44a 100644 --- a/bigquery/google/cloud/bigquery/dataset.py +++ b/bigquery/google/cloud/bigquery/dataset.py @@ -151,6 +151,46 @@ def table(self, table_id): """ return TableReference(self, table_id) + @classmethod + def from_api_repr(cls, resource): + project = resource['projectId'] + dataset_id = resource['datasetId'] + return cls(project, dataset_id) + + def to_api_repr(self): + return { + 'projectId': self._project, + 'datasetId': self._dataset_id, + } + + def _key(self): + """A tuple key that unique-ly describes this field. + + Used to compute this instance's hashcode and evaluate equality. + + Returns: + tuple: The contents of this :class:`DatasetReference`. + """ + return ( + self._project, + self._dataset_id, + ) + + def __eq__(self, other): + if not isinstance(other, DatasetReference): + return NotImplemented + return self._key() == other._key() + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._key()) + + def __repr__(self): + return 'DatasetReference{}'.format(self._key()) + + class Dataset(object): """Datasets are containers for tables. diff --git a/bigquery/google/cloud/bigquery/table.py b/bigquery/google/cloud/bigquery/table.py index 87112ada3ea1..03e945583e6e 100644 --- a/bigquery/google/cloud/bigquery/table.py +++ b/bigquery/google/cloud/bigquery/table.py @@ -105,6 +105,63 @@ def path(self): return '/projects/%s/datasets/%s/tables/%s' % ( self._project, self._dataset_id, self._table_id) + @classmethod + def from_api_repr(cls, resource): + """Factory: construct a table reference given its API representation + + :type resource: dict + :param resource: table reference representation returned from the API + + :rtype: :class:`google.cloud.bigquery.table.TableReference` + :returns: Table reference parsed from ``resource``. + """ + from google.cloud.bigquery.dataset import DatasetReference + + project = resource['projectId'] + dataset_id = resource['datasetId'] + table_id = resource['tableId'] + return cls(DatasetReference(project, dataset_id), table_id) + + def to_api_repr(self): + """Construct the API resource representation of this table reference. + + :rtype: dict + :returns: Table reference as represented as an API resource + """ + return { + 'projectId': self._project, + 'datasetId': self._dataset_id, + 'tableId': self._table_id, + } + + def _key(self): + """A tuple key that unique-ly describes this field. + + Used to compute this instance's hashcode and evaluate equality. + + Returns: + tuple: The contents of this :class:`DatasetReference`. + """ + return ( + self._project, + self._dataset_id, + self._table_id, + ) + + def __eq__(self, other): + if not isinstance(other, TableReference): + return NotImplemented + return self._key() == other._key() + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._key()) + + def __repr__(self): + return 'TableReference{}'.format(self._key()) + class Table(object): """Tables represent a set of rows whose values correspond to a schema. diff --git a/bigquery/tests/unit/test_dataset.py b/bigquery/tests/unit/test_dataset.py index ced77990a65d..93e4dced594f 100644 --- a/bigquery/tests/unit/test_dataset.py +++ b/bigquery/tests/unit/test_dataset.py @@ -114,6 +114,70 @@ def test_table(self): self.assertEqual(table_ref.project, 'some-project-1') self.assertEqual(table_ref.table_id, 'table_1') + def test_to_api_repr(self): + dataset = self._make_one('project_1', 'dataset_1') + + resource = dataset.to_api_repr() + + self.assertEqual( + resource, + { + 'projectId': 'project_1', + 'datasetId': 'dataset_1', + }) + + def test_from_api_repr(self): + from google.cloud.bigquery.dataset import DatasetReference + expected = self._make_one('project_1', 'dataset_1') + + got = DatasetReference.from_api_repr( + { + 'projectId': 'project_1', + 'datasetId': 'dataset_1', + }) + + self.assertEqual(expected, got) + + def test___eq___wrong_type(self): + dataset = self._make_one('project_1', 'dataset_1') + other = object() + self.assertNotEqual(dataset, other) + self.assertEqual(dataset, mock.ANY) + + def test___eq___project_mismatch(self): + dataset = self._make_one('project_1', 'dataset_1') + other = self._make_one('project_2', 'dataset_1') + self.assertNotEqual(dataset, other) + + def test___eq___dataset_mismatch(self): + dataset = self._make_one('project_1', 'dataset_1') + other = self._make_one('project_1', 'dataset_2') + self.assertNotEqual(dataset, other) + + def test___eq___equality(self): + dataset = self._make_one('project_1', 'dataset_1') + other = self._make_one('project_1', 'dataset_1') + self.assertEqual(dataset, other) + + def test___hash__set_equality(self): + dataset1 = self._make_one('project_1', 'dataset_1') + dataset2 = self._make_one('project_1', 'dataset_2') + set_one = {dataset1, dataset2} + set_two = {dataset1, dataset2} + self.assertEqual(set_one, set_two) + + def test___hash__not_equals(self): + dataset1 = self._make_one('project_1', 'dataset_1') + dataset2 = self._make_one('project_1', 'dataset_2') + set_one = {dataset1} + set_two = {dataset2} + self.assertNotEqual(set_one, set_two) + + def test___repr__(self): + dataset = self._make_one('project1', 'dataset1') + expected = "DatasetReference('project1', 'dataset1')" + self.assertEqual(repr(dataset), expected) + class TestDataset(unittest.TestCase): from google.cloud.bigquery.dataset import DatasetReference diff --git a/bigquery/tests/unit/test_table.py b/bigquery/tests/unit/test_table.py index 2327d11b1ed3..e71794513802 100644 --- a/bigquery/tests/unit/test_table.py +++ b/bigquery/tests/unit/test_table.py @@ -58,6 +58,98 @@ def test_ctor_defaults(self): self.assertEqual(table_ref.dataset_id, dataset_ref.dataset_id) self.assertEqual(table_ref.table_id, 'table_1') + def test_to_api_repr(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset_ref = DatasetReference('project_1', 'dataset_1') + table_ref = self._make_one(dataset_ref, 'table_1') + + resource = table_ref.to_api_repr() + + self.assertEqual( + resource, + { + 'projectId': 'project_1', + 'datasetId': 'dataset_1', + 'tableId': 'table_1', + }) + + def test_from_api_repr(self): + from google.cloud.bigquery.dataset import DatasetReference + from google.cloud.bigquery.table import TableReference + dataset_ref = DatasetReference('project_1', 'dataset_1') + expected = self._make_one(dataset_ref, 'table_1') + + got = TableReference.from_api_repr( + { + 'projectId': 'project_1', + 'datasetId': 'dataset_1', + 'tableId': 'table_1', + }) + + self.assertEqual(expected, got) + + def test___eq___wrong_type(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset_ref = DatasetReference('project_1', 'dataset_1') + table = self._make_one(dataset_ref, 'table_1') + other = object() + self.assertNotEqual(table, other) + self.assertEqual(table, mock.ANY) + + def test___eq___project_mismatch(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset = DatasetReference('project_1', 'dataset_1') + other_dataset = DatasetReference('project_2', 'dataset_1') + table = self._make_one(dataset, 'table_1') + other = self._make_one(other_dataset, 'table_1') + self.assertNotEqual(table, other) + + def test___eq___dataset_mismatch(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset = DatasetReference('project_1', 'dataset_1') + other_dataset = DatasetReference('project_1', 'dataset_2') + table = self._make_one(dataset, 'table_1') + other = self._make_one(other_dataset, 'table_1') + self.assertNotEqual(table, other) + + def test___eq___table_mismatch(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset = DatasetReference('project_1', 'dataset_1') + table = self._make_one(dataset, 'table_1') + other = self._make_one(dataset, 'table_2') + self.assertNotEqual(table, other) + + def test___eq___equality(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset = DatasetReference('project_1', 'dataset_1') + table = self._make_one(dataset, 'table_1') + other = self._make_one(dataset, 'table_1') + self.assertEqual(table, other) + + def test___hash__set_equality(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset = DatasetReference('project_1', 'dataset_1') + table1 = self._make_one(dataset, 'table1') + table2 = self._make_one(dataset, 'table2') + set_one = {table1, table2} + set_two = {table1, table2} + self.assertEqual(set_one, set_two) + + def test___hash__not_equals(self): + from google.cloud.bigquery.dataset import DatasetReference + dataset = DatasetReference('project_1', 'dataset_1') + table1 = self._make_one(dataset, 'table1') + table2 = self._make_one(dataset, 'table2') + set_one = {table1} + set_two = {table2} + self.assertNotEqual(set_one, set_two) + + def test___repr__(self): + dataset = DatasetReference('project1', 'dataset1') + table1 = self._make_one(dataset, 'table1') + expected = "TableReference('project1', 'dataset1', 'table1')" + self.assertEqual(repr(table1), expected) + class TestTable(unittest.TestCase, _SchemaBase): From 97d63194ea12958a24a74ec02b61c61d5505a41d Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Thu, 21 Sep 2017 14:18:00 -0700 Subject: [PATCH 2/3] Fix lint errors. --- bigquery/google/cloud/bigquery/dataset.py | 1 - bigquery/tests/unit/test_dataset.py | 2 +- bigquery/tests/unit/test_table.py | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bigquery/google/cloud/bigquery/dataset.py b/bigquery/google/cloud/bigquery/dataset.py index 0f945b7fb44a..9af52232b970 100644 --- a/bigquery/google/cloud/bigquery/dataset.py +++ b/bigquery/google/cloud/bigquery/dataset.py @@ -191,7 +191,6 @@ def __repr__(self): return 'DatasetReference{}'.format(self._key()) - class Dataset(object): """Datasets are containers for tables. diff --git a/bigquery/tests/unit/test_dataset.py b/bigquery/tests/unit/test_dataset.py index 93e4dced594f..c04d154b52da 100644 --- a/bigquery/tests/unit/test_dataset.py +++ b/bigquery/tests/unit/test_dataset.py @@ -135,7 +135,7 @@ def test_from_api_repr(self): 'projectId': 'project_1', 'datasetId': 'dataset_1', }) - + self.assertEqual(expected, got) def test___eq___wrong_type(self): diff --git a/bigquery/tests/unit/test_table.py b/bigquery/tests/unit/test_table.py index e71794513802..f2c2297d244b 100644 --- a/bigquery/tests/unit/test_table.py +++ b/bigquery/tests/unit/test_table.py @@ -85,7 +85,7 @@ def test_from_api_repr(self): 'datasetId': 'dataset_1', 'tableId': 'table_1', }) - + self.assertEqual(expected, got) def test___eq___wrong_type(self): @@ -125,7 +125,7 @@ def test___eq___equality(self): table = self._make_one(dataset, 'table_1') other = self._make_one(dataset, 'table_1') self.assertEqual(table, other) - + def test___hash__set_equality(self): from google.cloud.bigquery.dataset import DatasetReference dataset = DatasetReference('project_1', 'dataset_1') From 58a80306575ae7eabe02811b8b6bbfe8d14a1bbc Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Thu, 21 Sep 2017 15:19:38 -0700 Subject: [PATCH 3/3] Replace unique-ly with uniquely. --- bigquery/google/cloud/bigquery/dataset.py | 2 +- bigquery/google/cloud/bigquery/schema.py | 2 +- bigquery/google/cloud/bigquery/table.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bigquery/google/cloud/bigquery/dataset.py b/bigquery/google/cloud/bigquery/dataset.py index 9af52232b970..e464fcfb93bd 100644 --- a/bigquery/google/cloud/bigquery/dataset.py +++ b/bigquery/google/cloud/bigquery/dataset.py @@ -164,7 +164,7 @@ def to_api_repr(self): } def _key(self): - """A tuple key that unique-ly describes this field. + """A tuple key that uniquely describes this field. Used to compute this instance's hashcode and evaluate equality. diff --git a/bigquery/google/cloud/bigquery/schema.py b/bigquery/google/cloud/bigquery/schema.py index 4aea34ac22e0..535c445a3726 100644 --- a/bigquery/google/cloud/bigquery/schema.py +++ b/bigquery/google/cloud/bigquery/schema.py @@ -126,7 +126,7 @@ def to_api_repr(self): return answer def _key(self): - """A tuple key that unique-ly describes this field. + """A tuple key that uniquely describes this field. Used to compute this instance's hashcode and evaluate equality. diff --git a/bigquery/google/cloud/bigquery/table.py b/bigquery/google/cloud/bigquery/table.py index 03e945583e6e..542e87db0bb8 100644 --- a/bigquery/google/cloud/bigquery/table.py +++ b/bigquery/google/cloud/bigquery/table.py @@ -135,7 +135,7 @@ def to_api_repr(self): } def _key(self): - """A tuple key that unique-ly describes this field. + """A tuple key that uniquely describes this field. Used to compute this instance's hashcode and evaluate equality.