Repository: googleapis/python-bigquery

Issue:
_from_api_repr_scalar fails, if parameter value is None
Tested on latest (2.6.0) version, using python 3.8 on linux.

If `ArrayQueryParameter` contaning at least one `None` value is added to `query_parameters`, when using `job.query_parameters`  (result after submitting the job) `'NoneType' object has no attribute 'mode'` will be raised.

This is because:
- `_from_api_repr_scalar` is called on `ArrayQueryParameter`
- `_QUERY_PARAMS_FROM_JSON[array_type](value, None) for value in values`, the `value` is None
- this corresponds to `_int_from_json(None, None)`
- `_not_null(None, None)` is called
- `return value is not None or field.mode != "NULLABLE"` raises exception

Stack trace:
```
AttributeError: 'NoneType' object has no attribute 'mode'
  (snip)
  File "(snip)/site-packages/google/cloud/bigquery/job/query.py", line 632, in query_parameters
    return self._configuration.query_parameters
  File "(snip)/site-packages/google/cloud/bigquery/job/query.py", line 314, in query_parameters
    return _from_api_repr_query_parameters(prop)
  File "(snip)/site-packages/google/cloud/bigquery/job/query.py", line 79, in _from_api_repr_query_parameters
    return [_query_param_from_api_repr(mapping) for mapping in resource]
  File "(snip)/site-packages/google/cloud/bigquery/job/query.py", line 79, in <listcomp>
    return [_query_param_from_api_repr(mapping) for mapping in resource]
  File "(snip)/site-packages/google/cloud/bigquery/query.py", line 632, in _query_param_from_api_repr
    return klass.from_api_repr(resource)
  File "(snip)/site-packages/google/cloud/bigquery/query.py", line 257, in from_api_repr
    return cls._from_api_repr_scalar(resource)
  File "(snip)/site-packages/google/cloud/bigquery/query.py", line 239, in _from_api_repr_scalar
    converted = [
  File "(snip)/site-packages/google/cloud/bigquery/query.py", line 240, in <listcomp>
    _QUERY_PARAMS_FROM_JSON[array_type](value, None) for value in values
  File "(snip)/site-packages/google/cloud/bigquery/_helpers.py", line 48, in _int_from_json
    if _not_null(value, field):
  File "(snip)/site-packages/google/cloud/bigquery/_helpers.py", line 43, in _not_null
    return value is not None or field.mode != "NULLABLE"
```



Discussion:

Resolving patch:
diff --git a/google/cloud/bigquery/_helpers.py b/google/cloud/bigquery/_helpers.py
--- a/google/cloud/bigquery/_helpers.py
+++ b/google/cloud/bigquery/_helpers.py
@@ -40,7 +40,7 @@
 
 def _not_null(value, field):
     """Check whether 'value' should be coerced to 'field' type."""
-    return value is not None or field.mode != "NULLABLE"
+    return value is not None or (field is not None and field.mode != "NULLABLE")
 
 
 def _int_from_json(value, field):


Test patch:
diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py
--- a/tests/unit/test_query.py
+++ b/tests/unit/test_query.py
@@ -383,6 +383,16 @@ def test_from_api_repr_wo_values(self):
         self.assertEqual(param.array_type, "INT64")
         self.assertEqual(param.values, [])
 
+    def test_from_api_repr_w_none_values(self):
+        RESOURCE = {
+            "parameterType": {"type": "ARRAY", "arrayType": {"type": "INT64"}},
+            "parameterValue": {"arrayValues": [{"value": "1"}, {"value": None}]},
+        }
+        klass = self._get_target_class()
+        param = klass.from_api_repr(RESOURCE)
+        self.assertEqual(param.array_type, "INT64")
+        self.assertEqual(param.values, [1, None])
+
     def test_from_api_repr_w_struct_type(self):
         from google.cloud.bigquery.query import StructQueryParameter