From dc3f531464bf7997fb3b5c32826d7269074c4d04 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Thu, 7 Feb 2019 13:40:38 -0500 Subject: [PATCH] Allow nested records w/ null values. (#7297) Adds explicit unit tests for helpers added in #7022. Closes #7294. --- bigquery/google/cloud/bigquery/_helpers.py | 5 +- bigquery/tests/unit/test__helpers.py | 121 +++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/bigquery/google/cloud/bigquery/_helpers.py b/bigquery/google/cloud/bigquery/_helpers.py index 10753cfc998b..72e1fa276dd7 100644 --- a/bigquery/google/cloud/bigquery/_helpers.py +++ b/bigquery/google/cloud/bigquery/_helpers.py @@ -398,7 +398,10 @@ def _record_field_to_json(fields, row_value): for subindex, subfield in enumerate(fields): subname = subfield.name - subvalue = row_value[subname] if isdict else row_value[subindex] + if isdict: + subvalue = row_value.get(subname) + else: + subvalue = row_value[subindex] record[subname] = _field_to_json(subfield, subvalue) return record diff --git a/bigquery/tests/unit/test__helpers.py b/bigquery/tests/unit/test__helpers.py index c2c4f9f7f787..3884695d83af 100644 --- a/bigquery/tests/unit/test__helpers.py +++ b/bigquery/tests/unit/test__helpers.py @@ -776,6 +776,127 @@ def test_w_datetime(self): self.assertEqual(self._call_fut(when), "12:13:41") +def _make_field(field_type, mode="NULLABLE", name="testing", fields=()): + from google.cloud.bigquery.schema import SchemaField + + return SchemaField(name=name, field_type=field_type, mode=mode, fields=fields) + + +class Test_scalar_field_to_json(unittest.TestCase): + def _call_fut(self, field, value): + from google.cloud.bigquery._helpers import _scalar_field_to_json + + return _scalar_field_to_json(field, value) + + def test_w_unknown_field_type(self): + field = _make_field("UNKNOWN") + original = object() + converted = self._call_fut(field, original) + self.assertIs(converted, original) + + def test_w_known_field_type(self): + field = _make_field("INT64") + original = 42 + converted = self._call_fut(field, original) + self.assertEqual(converted, str(original)) + + +class Test_repeated_field_to_json(unittest.TestCase): + def _call_fut(self, field, value): + from google.cloud.bigquery._helpers import _repeated_field_to_json + + return _repeated_field_to_json(field, value) + + def test_w_empty(self): + field = _make_field("INT64", mode="REPEATED") + original = [] + converted = self._call_fut(field, original) + self.assertEqual(converted, original) + self.assertEqual(field.mode, "REPEATED") + + def test_w_non_empty(self): + field = _make_field("INT64", mode="REPEATED") + original = [42] + converted = self._call_fut(field, original) + self.assertEqual(converted, [str(value) for value in original]) + self.assertEqual(field.mode, "REPEATED") + + +class Test_record_field_to_json(unittest.TestCase): + def _call_fut(self, field, value): + from google.cloud.bigquery._helpers import _record_field_to_json + + return _record_field_to_json(field, value) + + def test_w_empty(self): + fields = [] + original = [] + converted = self._call_fut(fields, original) + self.assertEqual(converted, {}) + + def test_w_non_empty_list(self): + fields = [ + _make_field("INT64", name="one", mode="NULLABLE"), + _make_field("STRING", name="two", mode="NULLABLE"), + ] + original = [42, "two"] + converted = self._call_fut(fields, original) + self.assertEqual(converted, {"one": "42", "two": "two"}) + + def test_w_non_empty_dict(self): + fields = [ + _make_field("INT64", name="one", mode="NULLABLE"), + _make_field("STRING", name="two", mode="NULLABLE"), + ] + original = {"one": 42, "two": "two"} + converted = self._call_fut(fields, original) + self.assertEqual(converted, {"one": "42", "two": "two"}) + + def test_w_missing_nullable(self): + fields = [ + _make_field("INT64", name="one", mode="NULLABLE"), + _make_field("STRING", name="two", mode="NULLABLE"), + ] + original = {"one": 42} + converted = self._call_fut(fields, original) + self.assertEqual(converted, {"one": "42", "two": None}) + + +class Test_field_to_json(unittest.TestCase): + def _call_fut(self, field, value): + from google.cloud.bigquery._helpers import _field_to_json + + return _field_to_json(field, value) + + def test_w_none(self): + field = _make_field("INT64") + original = None + converted = self._call_fut(field, original) + self.assertIsNone(converted) + + def test_w_repeated(self): + field = _make_field("INT64", mode="REPEATED") + original = [42, 17] + converted = self._call_fut(field, original) + self.assertEqual(converted, [str(value) for value in original]) + + def test_w_record(self): + subfields = [ + _make_field("INT64", name="one"), + _make_field("STRING", name="two"), + ] + field = _make_field("RECORD", fields=subfields) + original = {"one": 42, "two": "two"} + converted = self._call_fut(field, original) + self.assertEqual(converted, {"one": "42", "two": "two"}) + + def test_w_scalar(self): + field = _make_field("INT64") + original = 42 + converted = self._call_fut(field, original) + self.assertEqual(converted, str(original)) + + class Test_snake_to_camel_case(unittest.TestCase): def _call_fut(self, value): from google.cloud.bigquery._helpers import _snake_to_camel_case