diff --git a/bigquery/gcloud/aio/bigquery/utils.py b/bigquery/gcloud/aio/bigquery/utils.py index 0216384d4..118346301 100644 --- a/bigquery/gcloud/aio/bigquery/utils.py +++ b/bigquery/gcloud/aio/bigquery/utils.py @@ -101,8 +101,10 @@ def parse(field: dict[str, Any], value: Any) -> Any: ) raise + value = flatten(value) + if field['mode'] == 'NULLABLE' and value is None: - return value + return None if field['mode'] == 'REPEATED': if field['type'] == 'RECORD': @@ -110,17 +112,17 @@ def parse(field: dict[str, Any], value: Any) -> Any: f['name']: parse(f, x) for f, x in zip(field['fields'], xs) } - for xs in flatten(value)] + for xs in value] - return [convert(x) for x in flatten(value)] + return [convert(x) for x in value] if field['type'] == 'RECORD': return { f['name']: parse(f, x) - for f, x in zip(field['fields'], flatten(value)) + for f, x in zip(field['fields'], value) } - return convert(flatten(value)) + return convert(value) def query_response_to_dict(response: dict[str, Any]) -> list[dict[str, Any]]: diff --git a/bigquery/tests/unit/utils_test.py b/bigquery/tests/unit/utils_test.py index fdd158009..3320c1c9d 100644 --- a/bigquery/tests/unit/utils_test.py +++ b/bigquery/tests/unit/utils_test.py @@ -105,6 +105,8 @@ def test_parse_nullable(kind): # make sure we never convert to a falsey typed equivalent # eg. for BOOLEAN, None != False assert parse(field, None) is None + # BigQuery API wraps null values as {'v': None} -- must not crash + assert parse(field, {'v': None}) is None @pytest.mark.parametrize(