Skip to content

Commit 25b988f

Browse files
authored
Don't raise error when encountering unknown fields in Models API. (googleapis#8083)
As new fields are added, the JSON -> Protobuf conversion should not fail. Instead, it should ignore unknown fields. So that this data is not discarded, use `_properties` as is the convention in our REST libraries. It's private, but can be used as a workaround to get access to fields that haven't yet been added to the client library.
1 parent 42a05e5 commit 25b988f

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

bigquery/google/cloud/bigquery/model.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ def from_api_repr(cls, resource):
268268
google.cloud.bigquery.model.Model: Model parsed from ``resource``.
269269
"""
270270
this = cls(None)
271+
# Keep a reference to the resource as a workaround to find unknown
272+
# field values.
273+
this._properties = resource
271274

272275
# Convert from millis-from-epoch to timestamp well-known type.
273276
# TODO: Remove this hack once CL 238585470 hits prod.
@@ -279,12 +282,9 @@ def from_api_repr(cls, resource):
279282
start_time = datetime_helpers.from_microseconds(1e3 * float(start_time))
280283
training_run["startTime"] = datetime_helpers.to_rfc3339(start_time)
281284

282-
this._proto = json_format.ParseDict(resource, types.Model())
283-
for key in six.itervalues(cls._PROPERTY_TO_API_FIELD):
284-
# Leave missing keys unset. This allows us to use setdefault in the
285-
# getters where we want a default value other than None.
286-
if key in resource:
287-
this._properties[key] = resource[key]
285+
this._proto = json_format.ParseDict(
286+
resource, types.Model(), ignore_unknown_fields=True
287+
)
288288
return this
289289

290290
def _build_resource(self, filter_fields):
@@ -304,6 +304,7 @@ class ModelReference(object):
304304

305305
def __init__(self):
306306
self._proto = types.ModelReference()
307+
self._properties = {}
307308

308309
@property
309310
def project(self):
@@ -342,7 +343,12 @@ def from_api_repr(cls, resource):
342343
Model reference parsed from ``resource``.
343344
"""
344345
ref = cls()
345-
ref._proto = json_format.ParseDict(resource, types.ModelReference())
346+
# Keep a reference to the resource as a workaround to find unknown
347+
# field values.
348+
ref._properties = resource
349+
ref._proto = json_format.ParseDict(
350+
resource, types.ModelReference(), ignore_unknown_fields=True
351+
)
346352
return ref
347353

348354
@classmethod

bigquery/tests/unit/model/test_model.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,22 @@ def test_from_api_repr_w_minimal_resource(target_class):
165165
assert len(got.label_columns) == 0
166166

167167

168+
def test_from_api_repr_w_unknown_fields(target_class):
169+
from google.cloud.bigquery import ModelReference
170+
171+
resource = {
172+
"modelReference": {
173+
"projectId": "my-project",
174+
"datasetId": "my_dataset",
175+
"modelId": "my_model",
176+
},
177+
"thisFieldIsNotInTheProto": "just ignore me",
178+
}
179+
got = target_class.from_api_repr(resource)
180+
assert got.reference == ModelReference.from_string("my-project.my_dataset.my_model")
181+
assert got._properties is resource
182+
183+
168184
@pytest.mark.parametrize(
169185
"resource,filter_fields,expected",
170186
[

bigquery/tests/unit/model/test_model_reference.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ def test_from_api_repr(target_class):
3737
assert got.path == "/projects/my-project/datasets/my_dataset/models/my_model"
3838

3939

40+
def test_from_api_repr_w_unknown_fields(target_class):
41+
resource = {
42+
"projectId": "my-project",
43+
"datasetId": "my_dataset",
44+
"modelId": "my_model",
45+
"thisFieldIsNotInTheProto": "just ignore me",
46+
}
47+
got = target_class.from_api_repr(resource)
48+
assert got.project == "my-project"
49+
assert got.dataset_id == "my_dataset"
50+
assert got.model_id == "my_model"
51+
assert got._properties is resource
52+
53+
4054
def test_to_api_repr(target_class):
4155
ref = target_class.from_string("my-project.my_dataset.my_model")
4256
got = ref.to_api_repr()

0 commit comments

Comments
 (0)