ortools.math_opt.python.normalize

Utility functions for normalizing proto3 message objects in Python.

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

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 all empty fields from message.

This is useful for testing.

Arguments:
  • protobuf_message: The Message object to clear.