ortools.math_opt.python.normalize
Utility functions for normalizing proto3 message objects in Python.
1#!/usr/bin/env python3 2# Copyright 2010-2025 Google LLC 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# A fork of net/proto2/contrib/pyutil/normalize.py. A lot of the code can be 16# deleted because we do not support proto2 (no groups, no extension). Further, 17# the code has been changed to not clear: 18# * optional scalar fields at their default value. 19# * durations 20# * messages in a oneof 21 22 23"""Utility functions for normalizing proto3 message objects in Python.""" 24from google.protobuf import duration_pb2 25from google.protobuf import descriptor 26from google.protobuf import message 27 28 29def math_opt_normalize_proto(protobuf_message: message.Message) -> None: 30 """Clears all non-duration submessages that are not in one_ofs. 31 32 A message is considered `empty` if: 33 * every non-optional scalar fields has its default value, 34 * every optional scalar field is unset, 35 * every repeated/map fields is empty 36 * every oneof is unset, 37 * every duration field is unset 38 * all other message fields (singular, not oneof, not duration) are `empty`. 39 This function clears all `empty` fields from `message`. 40 41 This is useful for testing. 42 43 Args: 44 protobuf_message: The Message object to clear. 45 """ 46 for field, value in protobuf_message.ListFields(): 47 if field.type != field.TYPE_MESSAGE: 48 continue 49 if field.label == field.LABEL_REPEATED: 50 # Now the repeated case, recursively normalize each member. Note that 51 # there is no field presence for repeated fields, so we don't need to call 52 # ClearField(). 53 # 54 # Maps need to be handled specially. 55 if ( 56 field.message_type.has_options 57 and field.message_type.GetOptions().map_entry 58 ): 59 if ( 60 field.message_type.fields_by_number[2].type 61 == descriptor.FieldDescriptor.TYPE_MESSAGE 62 ): 63 for item in value.values(): 64 math_opt_normalize_proto(item) 65 # The remaining case is a regular repeated field (a list). 66 else: 67 for item in value: 68 math_opt_normalize_proto(item) 69 continue 70 # Last case, the non-repeated sub-message 71 math_opt_normalize_proto(value) 72 # If field value is empty, not a Duration, and not in a oneof, clear it. 73 if ( 74 not value.ListFields() 75 and field.message_type != duration_pb2.Duration.DESCRIPTOR 76 and field.containing_oneof is None 77 ): 78 protobuf_message.ClearField(field.name)
def
math_opt_normalize_proto(protobuf_message: google.protobuf.message.Message) -> None:
30def math_opt_normalize_proto(protobuf_message: message.Message) -> None: 31 """Clears all non-duration submessages that are not in one_ofs. 32 33 A message is considered `empty` if: 34 * every non-optional scalar fields has its default value, 35 * every optional scalar field is unset, 36 * every repeated/map fields is empty 37 * every oneof is unset, 38 * every duration field is unset 39 * all other message fields (singular, not oneof, not duration) are `empty`. 40 This function clears all `empty` fields from `message`. 41 42 This is useful for testing. 43 44 Args: 45 protobuf_message: The Message object to clear. 46 """ 47 for field, value in protobuf_message.ListFields(): 48 if field.type != field.TYPE_MESSAGE: 49 continue 50 if field.label == field.LABEL_REPEATED: 51 # Now the repeated case, recursively normalize each member. Note that 52 # there is no field presence for repeated fields, so we don't need to call 53 # ClearField(). 54 # 55 # Maps need to be handled specially. 56 if ( 57 field.message_type.has_options 58 and field.message_type.GetOptions().map_entry 59 ): 60 if ( 61 field.message_type.fields_by_number[2].type 62 == descriptor.FieldDescriptor.TYPE_MESSAGE 63 ): 64 for item in value.values(): 65 math_opt_normalize_proto(item) 66 # The remaining case is a regular repeated field (a list). 67 else: 68 for item in value: 69 math_opt_normalize_proto(item) 70 continue 71 # Last case, the non-repeated sub-message 72 math_opt_normalize_proto(value) 73 # If field value is empty, not a Duration, and not in a oneof, clear it. 74 if ( 75 not value.ListFields() 76 and field.message_type != duration_pb2.Duration.DESCRIPTOR 77 and field.containing_oneof is None 78 ): 79 protobuf_message.ClearField(field.name)
Clears all non-duration submessages that are not in one_ofs.
A message is considered empty if:
- every non-optional scalar fields has its default value,
- every optional scalar field is unset,
- every repeated/map fields is empty
- every oneof is unset,
- every duration field is unset
- all other message fields (singular, not oneof, not duration) are
empty. This function clears allemptyfields frommessage.
This is useful for testing.
Arguments:
- protobuf_message: The Message object to clear.