Added is_closed to EnumDescriptor in protobuf python

PiperOrigin-RevId: 486274963
pull/10917/head
Jie Luo 2 years ago committed by Copybara-Service
parent e71376e314
commit da9de8d4d4
  1. 4
      protobuf_deps.bzl
  2. 13
      python/google/protobuf/descriptor.py
  3. 2
      python/google/protobuf/internal/python_message.py
  4. 10
      python/google/protobuf/internal/type_checkers.py
  5. 15
      python/google/protobuf/json_format.py
  6. 6
      python/google/protobuf/pyext/descriptor.cc
  7. 8
      python/google/protobuf/text_format.py

@ -135,6 +135,6 @@ def protobuf_deps():
_github_archive(
name = "upb",
repo = "https://github.com/protocolbuffers/upb",
commit = "9e2d7f02da5440bfb0dfb069f61baa278aa2fbf6",
sha256 = "9eb13368a136af314855e1497838cf3124846b6a73a7e7c882455a52b8c04662",
commit = "73661563dbb82bf7fdd614dd8da1186c0acc6b17",
sha256 = "0b2789aa957c665165fa66892a6402489d6491cb097391fd8ea5b5a248dbde35",
)

@ -66,6 +66,7 @@ if _USE_C_DESCRIPTORS:
# and make it return True when the descriptor is an instance of the extension
# type written in C++.
class DescriptorMetaclass(type):
def __instancecheck__(cls, obj):
if super(DescriptorMetaclass, cls).__instancecheck__(obj):
return True
@ -736,6 +737,18 @@ class EnumDescriptor(_NestedDescriptorBase):
# Values are reversed to ensure that the first alias is retained.
self.values_by_number = dict((v.number, v) for v in reversed(values))
@property
def is_closed(self):
"""If the enum is closed.
closed enum means:
- Has a fixed set of named values.
- Encountering values not in this set causes them to be treated as
unknown fields.
- The first value (i.e., the default) may be nonzero.
"""
return self.file.syntax == 'proto2'
def CopyToProto(self, proto):
"""Copies this to a descriptor_pb2.EnumDescriptorProto.

@ -308,7 +308,7 @@ def _AttachFieldHelpers(cls, field_descriptor):
tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype)
decode_type = field_descriptor.type
if (decode_type == _FieldDescriptor.TYPE_ENUM and
type_checkers.SupportsOpenEnums(field_descriptor)):
not field_descriptor.enum_type.is_closed):
decode_type = _FieldDescriptor.TYPE_INT32
oneof_descriptor = None

@ -75,10 +75,6 @@ def ToShortestFloat(original):
return rounded
def SupportsOpenEnums(field_descriptor):
return field_descriptor.containing_type.syntax == 'proto3'
def GetTypeChecker(field):
"""Returns a type checker for a message field of the specified types.
@ -93,11 +89,11 @@ def GetTypeChecker(field):
field.type == _FieldDescriptor.TYPE_STRING):
return UnicodeValueChecker()
if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM:
if SupportsOpenEnums(field):
if field.enum_type.is_closed:
return EnumValueChecker(field.enum_type)
else:
# When open enums are supported, any int32 can be assigned.
return _VALUE_CHECKERS[_FieldDescriptor.CPPTYPE_INT32]
else:
return EnumValueChecker(field.enum_type)
return _VALUE_CHECKERS[field.cpp_type]

@ -287,10 +287,11 @@ class _Printer(object):
if enum_value is not None:
return enum_value.name
else:
if field.file.syntax == 'proto3':
if field.enum_type.is_closed:
raise SerializeToJsonError('Enum field contains an integer value '
'which can not mapped to an enum value.')
else:
return value
raise SerializeToJsonError('Enum field contains an integer value '
'which can not mapped to an enum value.')
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
# Use base64 Data encoding for bytes
@ -799,11 +800,11 @@ def _ConvertScalarFieldValue(value, field, path, require_str=False):
raise ParseError('Invalid enum value {0} for enum type {1}'.format(
value, field.enum_type.full_name))
if enum_value is None:
if field.file.syntax == 'proto3':
# Proto3 accepts unknown enums.
if field.enum_type.is_closed:
raise ParseError('Invalid enum value {0} for enum type {1}'.format(
value, field.enum_type.full_name))
else:
return number
raise ParseError('Invalid enum value {0} for enum type {1}'.format(
value, field.enum_type.full_name))
return enum_value.number
except ParseError as e:
raise ParseError('{0} at {1}'.format(e, path))

@ -1182,6 +1182,11 @@ static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
Py_RETURN_FALSE;
}
}
static PyObject* GetIsClosed(PyBaseDescriptor* self, void* closure) {
return PyBool_FromLong(_GetDescriptor(self)->is_closed());
}
static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
void *closure) {
return CheckCalledFromGeneratedFile("has_options");
@ -1225,6 +1230,7 @@ static PyGetSetDef Getters[] = {
"Containing type"},
{"has_options", (getter)GetHasOptions, (setter)SetHasOptions,
"Has Options"},
{"is_closed", (getter)GetIsClosed, nullptr, "If the enum is closed"},
{"_options", (getter) nullptr, (setter)SetOptions, "Options"},
{"_serialized_options", (getter) nullptr, (setter)SetSerializedOptions,
"Serialized Options"},

@ -1852,12 +1852,8 @@ def ParseEnum(field, value):
raise ValueError('Enum type "%s" has no value named %s.' %
(enum_descriptor.full_name, value))
else:
# Numeric value.
if hasattr(field.file, 'syntax'):
# Attribute is checked for compatibility.
if field.file.syntax == 'proto3':
# Proto3 accept numeric unknown enums.
return number
if not field.enum_type.is_closed:
return number
enum_value = enum_descriptor.values_by_number.get(number, None)
if enum_value is None:
raise ValueError('Enum type "%s" has no value with number %d.' %

Loading…
Cancel
Save