From bcd7ecdb2a49e1d7103c14bb408caef399b53ea2 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 15:08:50 -0800 Subject: [PATCH 01/10] fix: ignore float_precision in protobuf 7.x --- proto/message.py | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/proto/message.py b/proto/message.py index 10a6f42d..acc78427 100644 --- a/proto/message.py +++ b/proto/message.py @@ -577,6 +577,8 @@ def to_dict( if both arguments are explicitly set. float_precision (Optional(int)): If set, use this to specify float field valid digits. Default is None. + [DEPRECATED] float_precision was removed in Protobuf 7.x, and will be ignored + in those versions always_print_fields_with_no_presence (Optional(bool)): If True, fields without presence (implicit presence scalars, repeated fields, and map fields) will always be serialized. Any field that supports presence is not affected by @@ -592,28 +594,31 @@ def to_dict( print_fields = cls._normalize_print_fields_without_presence( always_print_fields_with_no_presence, including_default_value_fields ) - + kwargs = { + "preserving_proto_field_name":preserving_proto_field_name, + "use_integers_for_enums":use_integers_for_enums, + "float_precision": float_precision, + } + + # The `including_default_value_fields` argument was removed from protobuf 5.x + # and replaced with `always_print_fields_with_no_presence` which very similar but has + # handles optional fields consistently by not affecting them. + # The old flag accidentally had inconsistent behavior between proto2 + # optional and proto3 optional fields. if PROTOBUF_VERSION[0] in ("3", "4"): - return MessageToDict( - cls.pb(instance), - including_default_value_fields=print_fields, - preserving_proto_field_name=preserving_proto_field_name, - use_integers_for_enums=use_integers_for_enums, - float_precision=float_precision, - ) + kwargs["including_default_value_fields"] = print_fields else: - # The `including_default_value_fields` argument was removed from protobuf 5.x - # and replaced with `always_print_fields_with_no_presence` which very similar but has - # handles optional fields consistently by not affecting them. - # The old flag accidentally had inconsistent behavior between proto2 - # optional and proto3 optional fields. - return MessageToDict( - cls.pb(instance), - always_print_fields_with_no_presence=print_fields, - preserving_proto_field_name=preserving_proto_field_name, - use_integers_for_enums=use_integers_for_enums, - float_precision=float_precision, + warnings.warn( + "The argument `float_precision` has been removed from Protobuf 7.x.", + DeprecationWarning, ) + kwargs["use_integers_for_enums"] = print_fields + + # float_precision removed in protobuf 7 + if int(PROTOBUF_VERSION[0]) > 7 and float_precision is not None: + del kwargs["float_precision"] + + return MessageToDict(cls.pb(instance), **kwargs) def copy_from(cls, instance, other): """Equivalent for protobuf.Message.CopyFrom From a27845c8e5e6b95874489f2de23c34c37fd61e95 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 15:29:04 -0800 Subject: [PATCH 02/10] fixed implementation --- proto/message.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/proto/message.py b/proto/message.py index acc78427..0052628e 100644 --- a/proto/message.py +++ b/proto/message.py @@ -607,15 +607,17 @@ def to_dict( # optional and proto3 optional fields. if PROTOBUF_VERSION[0] in ("3", "4"): kwargs["including_default_value_fields"] = print_fields + del kwargs[always_print_fields_with_no_presence] else: + kwargs["always_print_fields_with_no_presence"] = print_fields + del kwargs["including_default_value_fields]" + + # float_precision removed in protobuf 7 + if int(PROTOBUF_VERSION[0]) > 7 and float_precision is not None: warnings.warn( "The argument `float_precision` has been removed from Protobuf 7.x.", DeprecationWarning, ) - kwargs["use_integers_for_enums"] = print_fields - - # float_precision removed in protobuf 7 - if int(PROTOBUF_VERSION[0]) > 7 and float_precision is not None: del kwargs["float_precision"] return MessageToDict(cls.pb(instance), **kwargs) From d946a7aeacc4d98648edba9018d73ca77260630b Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 15:39:47 -0800 Subject: [PATCH 03/10] refactored into shared helper --- proto/message.py | 109 +++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 65 deletions(-) diff --git a/proto/message.py b/proto/message.py index 0052628e..cd843fbc 100644 --- a/proto/message.py +++ b/proto/message.py @@ -457,6 +457,46 @@ def _normalize_print_fields_without_presence( or including_default_value_fields ) + @staticmethod + def _to_map( + cls, + map_fn, + instance, + *, + including_default_value_fields=None, + float_precision=None, + always_print_fields_with_no_presence=None, + **kwargs, + ): + """ + Helper for logic for to_dict and to_json + """ + print_fields = cls._normalize_print_fields_without_presence( + always_print_fields_with_no_presence, including_default_value_fields + ) + + # The `including_default_value_fields` argument was removed from protobuf 5.x + # and replaced with `always_print_fields_with_no_presence` which very similar but has + # handles optional fields consistently by not affecting them. + # The old flag accidentally had inconsistent behavior between proto2 + # optional and proto3 optional fields. + if PROTOBUF_VERSION[0] in ("3", "4"): + kwargs["including_default_value_fields"] = print_fields + else: + kwargs["always_print_fields_with_no_presence"] = print_fields + + if float_precision: + # float_precision removed in protobuf 7 + if int(PROTOBUF_VERSION[0]) < 7: + kwargs["float_precision"] = float_precision + else: + warnings.warn( + "The argument `float_precision` has been removed from Protobuf 7.x.", + DeprecationWarning, + ) + + return map_fn(cls.pb(instance), **kwargs) + def to_json( cls, instance, @@ -491,7 +531,7 @@ def to_json( An indent level of 0 or negative will only insert newlines. Pass None for the most compact representation without newlines. float_precision (Optional(int)): If set, use this to specify float field valid digits. - Default is None. + Default is None. [DEPRECATED] float_precision was removed in Protobuf 7.x. always_print_fields_with_no_presence (Optional(bool)): If True, fields without presence (implicit presence scalars, repeated fields, and map fields) will always be serialized. Any field that supports presence is not affected by @@ -501,36 +541,7 @@ def to_json( Returns: str: The json string representation of the protocol buffer. """ - - print_fields = cls._normalize_print_fields_without_presence( - always_print_fields_with_no_presence, including_default_value_fields - ) - - if PROTOBUF_VERSION[0] in ("3", "4"): - return MessageToJson( - cls.pb(instance), - use_integers_for_enums=use_integers_for_enums, - including_default_value_fields=print_fields, - preserving_proto_field_name=preserving_proto_field_name, - sort_keys=sort_keys, - indent=indent, - float_precision=float_precision, - ) - else: - # The `including_default_value_fields` argument was removed from protobuf 5.x - # and replaced with `always_print_fields_with_no_presence` which very similar but has - # handles optional fields consistently by not affecting them. - # The old flag accidentally had inconsistent behavior between proto2 - # optional and proto3 optional fields. - return MessageToJson( - cls.pb(instance), - use_integers_for_enums=use_integers_for_enums, - always_print_fields_with_no_presence=print_fields, - preserving_proto_field_name=preserving_proto_field_name, - sort_keys=sort_keys, - indent=indent, - float_precision=float_precision, - ) + return cls._to_map(map_fn=MessageToJson, **locals()) def from_json(cls, payload, *, ignore_unknown_fields=False) -> "Message": """Given a json string representing an instance, @@ -576,9 +587,7 @@ def to_dict( This value must match `always_print_fields_with_no_presence`, if both arguments are explicitly set. float_precision (Optional(int)): If set, use this to specify float field valid digits. - Default is None. - [DEPRECATED] float_precision was removed in Protobuf 7.x, and will be ignored - in those versions + Default is None. [DEPRECATED] float_precision was removed in Protobuf 7.x. always_print_fields_with_no_presence (Optional(bool)): If True, fields without presence (implicit presence scalars, repeated fields, and map fields) will always be serialized. Any field that supports presence is not affected by @@ -590,37 +599,7 @@ def to_dict( Messages and map fields are represented as dicts, repeated fields are represented as lists. """ - - print_fields = cls._normalize_print_fields_without_presence( - always_print_fields_with_no_presence, including_default_value_fields - ) - kwargs = { - "preserving_proto_field_name":preserving_proto_field_name, - "use_integers_for_enums":use_integers_for_enums, - "float_precision": float_precision, - } - - # The `including_default_value_fields` argument was removed from protobuf 5.x - # and replaced with `always_print_fields_with_no_presence` which very similar but has - # handles optional fields consistently by not affecting them. - # The old flag accidentally had inconsistent behavior between proto2 - # optional and proto3 optional fields. - if PROTOBUF_VERSION[0] in ("3", "4"): - kwargs["including_default_value_fields"] = print_fields - del kwargs[always_print_fields_with_no_presence] - else: - kwargs["always_print_fields_with_no_presence"] = print_fields - del kwargs["including_default_value_fields]" - - # float_precision removed in protobuf 7 - if int(PROTOBUF_VERSION[0]) > 7 and float_precision is not None: - warnings.warn( - "The argument `float_precision` has been removed from Protobuf 7.x.", - DeprecationWarning, - ) - del kwargs["float_precision"] - - return MessageToDict(cls.pb(instance), **kwargs) + return cls._to_map(map_fn=MessageToDict, **locals()) def copy_from(cls, instance, other): """Equivalent for protobuf.Message.CopyFrom From 89e5ac2336b8b7904016cb12c272e488d8a11707 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 15:58:59 -0800 Subject: [PATCH 04/10] moved _to_map outside class --- proto/message.py | 84 ++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/proto/message.py b/proto/message.py index cd843fbc..351abe81 100644 --- a/proto/message.py +++ b/proto/message.py @@ -457,46 +457,6 @@ def _normalize_print_fields_without_presence( or including_default_value_fields ) - @staticmethod - def _to_map( - cls, - map_fn, - instance, - *, - including_default_value_fields=None, - float_precision=None, - always_print_fields_with_no_presence=None, - **kwargs, - ): - """ - Helper for logic for to_dict and to_json - """ - print_fields = cls._normalize_print_fields_without_presence( - always_print_fields_with_no_presence, including_default_value_fields - ) - - # The `including_default_value_fields` argument was removed from protobuf 5.x - # and replaced with `always_print_fields_with_no_presence` which very similar but has - # handles optional fields consistently by not affecting them. - # The old flag accidentally had inconsistent behavior between proto2 - # optional and proto3 optional fields. - if PROTOBUF_VERSION[0] in ("3", "4"): - kwargs["including_default_value_fields"] = print_fields - else: - kwargs["always_print_fields_with_no_presence"] = print_fields - - if float_precision: - # float_precision removed in protobuf 7 - if int(PROTOBUF_VERSION[0]) < 7: - kwargs["float_precision"] = float_precision - else: - warnings.warn( - "The argument `float_precision` has been removed from Protobuf 7.x.", - DeprecationWarning, - ) - - return map_fn(cls.pb(instance), **kwargs) - def to_json( cls, instance, @@ -541,7 +501,7 @@ def to_json( Returns: str: The json string representation of the protocol buffer. """ - return cls._to_map(map_fn=MessageToJson, **locals()) + return _message_to_map(map_fn=MessageToJson, **locals()) def from_json(cls, payload, *, ignore_unknown_fields=False) -> "Message": """Given a json string representing an instance, @@ -599,7 +559,7 @@ def to_dict( Messages and map fields are represented as dicts, repeated fields are represented as lists. """ - return cls._to_map(map_fn=MessageToDict, **locals()) + return _message_to_map(map_fn=MessageToDict, **locals()) def copy_from(cls, instance, other): """Equivalent for protobuf.Message.CopyFrom @@ -952,4 +912,44 @@ def pb(self) -> Type[message.Message]: return self._pb +def _message_to_map( + cls, + map_fn, + instance, + *, + including_default_value_fields=None, + float_precision=None, + always_print_fields_with_no_presence=None, + **kwargs, +): + """ + Helper for logic for Message.to_dict and Message.to_json + """ + print_fields = cls._normalize_print_fields_without_presence( + always_print_fields_with_no_presence, including_default_value_fields + ) + + # The `including_default_value_fields` argument was removed from protobuf 5.x + # and replaced with `always_print_fields_with_no_presence` which very similar but has + # handles optional fields consistently by not affecting them. + # The old flag accidentally had inconsistent behavior between proto2 + # optional and proto3 optional fields. + if PROTOBUF_VERSION[0] in ("3", "4"): + kwargs["including_default_value_fields"] = print_fields + else: + kwargs["always_print_fields_with_no_presence"] = print_fields + + if float_precision: + # float_precision removed in protobuf 7 + if int(PROTOBUF_VERSION[0]) < 7: + kwargs["float_precision"] = float_precision + else: + warnings.warn( + "The argument `float_precision` has been removed from Protobuf 7.x.", + DeprecationWarning, + ) + + return map_fn(cls.pb(instance), **kwargs) + + __all__ = ("Message",) From 41c84993ef8447870c25e950d3584f65efc0b228 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 16:15:28 -0800 Subject: [PATCH 05/10] improved PROTOBUF_VERSION in prep for 10+ --- proto/message.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/proto/message.py b/proto/message.py index 351abe81..650a8c88 100644 --- a/proto/message.py +++ b/proto/message.py @@ -36,6 +36,10 @@ PROTOBUF_VERSION = google.protobuf.__version__ +# extract the major version code +_separator_idx = PROTOBUF_VERSION.find('.') +_PROTOBUF_MAJOR_VERSION = PROTOBUF_VERSION[:_separator_idx] if _separator_idx != -1 else PROTOBUF_VERSION + _upb = has_upb() # Important to cache result here. @@ -383,7 +387,7 @@ def _warn_if_including_default_value_fields_is_used_protobuf_5( including_default_value_fields (Optional(bool)): The value of `including_default_value_fields` set by the user. """ if ( - PROTOBUF_VERSION[0] not in ("3", "4") + _PROTOBUF_MAJOR_VERSION not in ("3", "4") and including_default_value_fields is not None ): warnings.warn( @@ -918,30 +922,30 @@ def _message_to_map( instance, *, including_default_value_fields=None, - float_precision=None, always_print_fields_with_no_presence=None, + float_precision=None, **kwargs, ): """ Helper for logic for Message.to_dict and Message.to_json """ - print_fields = cls._normalize_print_fields_without_presence( - always_print_fields_with_no_presence, including_default_value_fields - ) # The `including_default_value_fields` argument was removed from protobuf 5.x # and replaced with `always_print_fields_with_no_presence` which very similar but has # handles optional fields consistently by not affecting them. # The old flag accidentally had inconsistent behavior between proto2 # optional and proto3 optional fields. - if PROTOBUF_VERSION[0] in ("3", "4"): + print_fields = cls._normalize_print_fields_without_presence( + always_print_fields_with_no_presence, including_default_value_fields + ) + if _PROTOBUF_MAJOR_VERSION in ("3", "4"): kwargs["including_default_value_fields"] = print_fields else: kwargs["always_print_fields_with_no_presence"] = print_fields if float_precision: # float_precision removed in protobuf 7 - if int(PROTOBUF_VERSION[0]) < 7: + if _PROTOBUF_MAJOR_VERSION in ("3", "4", "5", "6"): kwargs["float_precision"] = float_precision else: warnings.warn( From 3b4a2e096d799356d547981b9ee66e0198693919 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 16:22:36 -0800 Subject: [PATCH 06/10] updated test --- tests/test_json.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test_json.py b/tests/test_json.py index ae3cf59e..e39fe15a 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -253,7 +253,10 @@ class Squid(proto.Message): assert re.search(r"massKg.*name", j) -# TODO: https://github.com/googleapis/proto-plus-python/issues/390 +pytest.skipif( + int(proto.message._PROTOBUF_MAJOR_VERSION) >= 7, + "float_precision removed in protobuf 7.x" +) def test_json_float_precision(): class Squid(proto.Message): name = proto.Field(proto.STRING, number=1) @@ -263,3 +266,21 @@ class Squid(proto.Message): j = Squid.to_json(s, float_precision=3, indent=None) assert j == '{"name": "Steve", "massKg": 3.14}' + +pytest.skipif( + int(proto.message._PROTOBUF_MAJOR_VERSION) < 7, + "unsupported protobuf version for test" +) +def test_json_float_precision_7_plus(): + class Squid(proto.Message): + name = proto.Field(proto.STRING, number=1) + mass_kg = proto.Field(proto.FLOAT, number=2) + + s = Squid(name="Steve", mass_kg=3.14159265) + with pytest.warns(DeprecationWarning) as warnings: + j = Squid.to_json(s, float_precision=3, indent=None) + + assert j == '{"name": "Steve", "massKg": 3.14159265}' + + assert len(warnings) == 1 + assert "`float_precision` has been removed" in warnings[0].message.args[0] From 638c8f61699c39aa56df1f04752e05923f04c954 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 16:48:26 -0800 Subject: [PATCH 07/10] improved tests --- proto/message.py | 1 + tests/test_json.py | 21 ++++++++++----------- tests/test_message.py | 23 +++++++++++++++++++++-- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/proto/message.py b/proto/message.py index 650a8c88..62c2386c 100644 --- a/proto/message.py +++ b/proto/message.py @@ -951,6 +951,7 @@ def _message_to_map( warnings.warn( "The argument `float_precision` has been removed from Protobuf 7.x.", DeprecationWarning, + stacklevel=3, ) return map_fn(cls.pb(instance), **kwargs) diff --git a/tests/test_json.py b/tests/test_json.py index e39fe15a..c2468383 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -253,34 +253,33 @@ class Squid(proto.Message): assert re.search(r"massKg.*name", j) -pytest.skipif( - int(proto.message._PROTOBUF_MAJOR_VERSION) >= 7, - "float_precision removed in protobuf 7.x" -) def test_json_float_precision(): + if int(proto.message._PROTOBUF_MAJOR_VERSION) >= 7: + pytest.skip("float_precision was removed in protobuf 7.x") + class Squid(proto.Message): name = proto.Field(proto.STRING, number=1) mass_kg = proto.Field(proto.FLOAT, number=2) - s = Squid(name="Steve", mass_kg=3.14159265) + s = Squid(name="Steve", mass_kg=3.141592) j = Squid.to_json(s, float_precision=3, indent=None) assert j == '{"name": "Steve", "massKg": 3.14}' -pytest.skipif( - int(proto.message._PROTOBUF_MAJOR_VERSION) < 7, - "unsupported protobuf version for test" -) + def test_json_float_precision_7_plus(): + if int(proto.message._PROTOBUF_MAJOR_VERSION) < 7: + pytest.skip("unsupported protobuf version for test") + class Squid(proto.Message): name = proto.Field(proto.STRING, number=1) mass_kg = proto.Field(proto.FLOAT, number=2) - s = Squid(name="Steve", mass_kg=3.14159265) + s = Squid(name="Steve", mass_kg=3.141592) with pytest.warns(DeprecationWarning) as warnings: j = Squid.to_json(s, float_precision=3, indent=None) - assert j == '{"name": "Steve", "massKg": 3.14159265}' + assert j == '{"name": "Steve", "massKg": 3.141592}' assert len(warnings) == 1 assert "`float_precision` has been removed" in warnings[0].message.args[0] diff --git a/tests/test_message.py b/tests/test_message.py index 720995a9..36a0655e 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -326,17 +326,36 @@ class Color(proto.Enum): ) -# TODO: https://github.com/googleapis/proto-plus-python/issues/390 def test_serialize_to_dict_float_precision(): + if int(proto.message._PROTOBUF_MAJOR_VERSION) >= 7: + pytest.skip("float_precision was removed in protobuf 7.x") + class Squid(proto.Message): mass_kg = proto.Field(proto.FLOAT, number=1) - s = Squid(mass_kg=3.14159265) + s = Squid(mass_kg=3.141592) s_dict = Squid.to_dict(s, float_precision=3) assert s_dict["mass_kg"] == 3.14 +def test_serialize_to_dict_float_precision_7_plus(): + if int(proto.message._PROTOBUF_MAJOR_VERSION) < 7: + pytest.skip("unsupported protobuf version for test") + + class Squid(proto.Message): + mass_kg = proto.Field(proto.FLOAT, number=1) + + s = Squid(mass_kg=3.141592) + + with pytest.warns(DeprecationWarning) as warnings: + s_dict = Squid.to_dict(s, float_precision=3) + assert s_dict["mass_kg"] == pytest.approx(3.141592) + + assert len(warnings) == 1 + assert "`float_precision` has been removed" in warnings[0].message.args[0] + + def test_unknown_field_deserialize(): # This is a somewhat common setup: a client uses an older proto definition, # while the server sends the newer definition. The client still needs to be From e6ef3c7c4466ba67e04e975f51fc6c8746308b66 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 16:54:03 -0800 Subject: [PATCH 08/10] fixed style --- proto/message.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proto/message.py b/proto/message.py index 62c2386c..ed64050d 100644 --- a/proto/message.py +++ b/proto/message.py @@ -37,8 +37,10 @@ PROTOBUF_VERSION = google.protobuf.__version__ # extract the major version code -_separator_idx = PROTOBUF_VERSION.find('.') -_PROTOBUF_MAJOR_VERSION = PROTOBUF_VERSION[:_separator_idx] if _separator_idx != -1 else PROTOBUF_VERSION +_separator_idx = PROTOBUF_VERSION.find(".") +_PROTOBUF_MAJOR_VERSION = ( + PROTOBUF_VERSION[:_separator_idx] if _separator_idx != -1 else PROTOBUF_VERSION +) _upb = has_upb() # Important to cache result here. From 944c570d6d8885e5016513ef9ffa100a74dbca77 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 16:55:30 -0800 Subject: [PATCH 09/10] added no cover --- proto/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/message.py b/proto/message.py index ed64050d..24386431 100644 --- a/proto/message.py +++ b/proto/message.py @@ -949,7 +949,7 @@ def _message_to_map( # float_precision removed in protobuf 7 if _PROTOBUF_MAJOR_VERSION in ("3", "4", "5", "6"): kwargs["float_precision"] = float_precision - else: + else: # pragma: NO COVER warnings.warn( "The argument `float_precision` has been removed from Protobuf 7.x.", DeprecationWarning, From 586cb92aef0bbbf99a6b361eda3563f2222c5ada Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Fri, 23 Jan 2026 16:59:41 -0800 Subject: [PATCH 10/10] use partition instead of find --- proto/message.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/proto/message.py b/proto/message.py index 24386431..5533e99b 100644 --- a/proto/message.py +++ b/proto/message.py @@ -37,10 +37,7 @@ PROTOBUF_VERSION = google.protobuf.__version__ # extract the major version code -_separator_idx = PROTOBUF_VERSION.find(".") -_PROTOBUF_MAJOR_VERSION = ( - PROTOBUF_VERSION[:_separator_idx] if _separator_idx != -1 else PROTOBUF_VERSION -) +_PROTOBUF_MAJOR_VERSION = PROTOBUF_VERSION.partition(".")[0] _upb = has_upb() # Important to cache result here.