1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc. All rights reserved.
3#
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file or at
6# https://developers.google.com/open-source/licenses/bsd
7
8"""Descriptors essentially contain exactly the information found in a .proto
9
10file, in types that make this information accessible in Python.
11"""
12
13__author__ = 'robinson@google.com (Will Robinson)'
14
15import abc
16import binascii
17import os
18import threading
19import warnings
20
21from google.protobuf.internal import api_implementation
22
23_USE_C_DESCRIPTORS = False
24if api_implementation.Type() != 'python':
25 # pylint: disable=protected-access
26 _message = api_implementation._c_module
27 # TODO: Remove this import after fix api_implementation
28 if _message is None:
29 from google.protobuf.pyext import _message
30 _USE_C_DESCRIPTORS = True
31
32
33class Error(Exception):
34 """Base error for this module."""
35
36
37class TypeTransformationError(Error):
38 """Error transforming between python proto type and corresponding C++ type."""
39
40
41if _USE_C_DESCRIPTORS:
42 # This metaclass allows to override the behavior of code like
43 # isinstance(my_descriptor, FieldDescriptor)
44 # and make it return True when the descriptor is an instance of the extension
45 # type written in C++.
46 class DescriptorMetaclass(type):
47
48 def __instancecheck__(cls, obj):
49 if super(DescriptorMetaclass, cls).__instancecheck__(obj):
50 return True
51 if isinstance(obj, cls._C_DESCRIPTOR_CLASS):
52 return True
53 return False
54
55else:
56 # The standard metaclass; nothing changes.
57 DescriptorMetaclass = abc.ABCMeta
58
59
60class _Lock(object):
61 """Wrapper class of threading.Lock(), which is allowed by 'with'."""
62
63 def __new__(cls):
64 self = object.__new__(cls)
65 self._lock = threading.Lock() # pylint: disable=protected-access
66 return self
67
68 def __enter__(self):
69 self._lock.acquire()
70
71 def __exit__(self, exc_type, exc_value, exc_tb):
72 self._lock.release()
73
74
75_lock = threading.Lock()
76
77
78def _Deprecated(
79 name,
80 alternative='get/find descriptors from generated code or query the descriptor_pool',
81):
82 if _Deprecated.count > 0:
83 _Deprecated.count -= 1
84 warnings.warn(
85 'Call to deprecated %s, use %s instead.' % (name, alternative),
86 category=DeprecationWarning,
87 stacklevel=3,
88 )
89
90
91# These must match the values in descriptor.proto, but we can't use them
92# directly because we sometimes need to reference them in feature helpers
93# below *during* the build of descriptor.proto.
94_FEATURESET_MESSAGE_ENCODING_DELIMITED = 2
95_FEATURESET_FIELD_PRESENCE_IMPLICIT = 2
96_FEATURESET_FIELD_PRESENCE_LEGACY_REQUIRED = 3
97_FEATURESET_REPEATED_FIELD_ENCODING_PACKED = 1
98_FEATURESET_ENUM_TYPE_CLOSED = 2
99
100# Deprecated warnings will print 100 times at most which should be enough for
101# users to notice and do not cause timeout.
102_Deprecated.count = 100
103
104
105_internal_create_key = object()
106
107
108class DescriptorBase(metaclass=DescriptorMetaclass):
109 """Descriptors base class.
110
111 This class is the base of all descriptor classes. It provides common options
112 related functionality.
113
114 Attributes:
115 has_options: True if the descriptor has non-default options. Usually it is
116 not necessary to read this -- just call GetOptions() which will happily
117 return the default instance. However, it's sometimes useful for
118 efficiency, and also useful inside the protobuf implementation to avoid
119 some bootstrapping issues.
120 file (FileDescriptor): Reference to file info.
121 """
122
123 if _USE_C_DESCRIPTORS:
124 # The class, or tuple of classes, that are considered as "virtual
125 # subclasses" of this descriptor class.
126 _C_DESCRIPTOR_CLASS = ()
127
128 def __init__(self, file, options, serialized_options, options_class_name):
129 """Initialize the descriptor given its options message and the name of the
130
131 class of the options message. The name of the class is required in case
132 the options message is None and has to be created.
133 """
134 self._features = None
135 self.file = file
136 self._original_options = options
137 # These two fields are duplicated as a compatibility shim for old gencode
138 # that resets them. In 26.x (cl/580304039) we renamed _options to,
139 # _loaded_options breaking backwards compatibility.
140 self._options = self._loaded_options = None
141 self._options_class_name = options_class_name
142 self._serialized_options = serialized_options
143
144 # Does this descriptor have non-default options?
145 self.has_options = (self._original_options is not None) or (
146 self._serialized_options is not None
147 )
148
149 @property
150 @abc.abstractmethod
151 def _parent(self):
152 pass
153
154 def _InferLegacyFeatures(self, edition, options, features):
155 """Infers features from proto2/proto3 syntax so that editions logic can be used everywhere.
156
157 Args:
158 edition: The edition to infer features for.
159 options: The options for this descriptor that are being processed.
160 features: The feature set object to modify with inferred features.
161 """
162 pass
163
164 def _GetFeatures(self):
165 if not self._features:
166 self._LazyLoadOptions()
167 return self._features
168
169 def _ResolveFeatures(self, edition, raw_options):
170 """Resolves features from the raw options of this descriptor.
171
172 Args:
173 edition: The edition to use for feature defaults.
174 raw_options: The options for this descriptor that are being processed.
175
176 Returns:
177 A fully resolved feature set for making runtime decisions.
178 """
179 # pylint: disable=g-import-not-at-top
180 from google.protobuf import descriptor_pb2
181
182 if self._parent:
183 features = descriptor_pb2.FeatureSet()
184 features.CopyFrom(self._parent._GetFeatures())
185 else:
186 features = self.file.pool._CreateDefaultFeatures(edition)
187 unresolved = descriptor_pb2.FeatureSet()
188 unresolved.CopyFrom(raw_options.features)
189 self._InferLegacyFeatures(edition, raw_options, unresolved)
190 features.MergeFrom(unresolved)
191
192 # Use the feature cache to reduce memory bloat.
193 return self.file.pool._InternFeatures(features)
194
195 def _LazyLoadOptions(self):
196 """Lazily initializes descriptor options towards the end of the build."""
197 if self._options and self._loaded_options == self._options:
198 # If neither has been reset by gencode, use the cache.
199 return
200
201 # pylint: disable=g-import-not-at-top
202 from google.protobuf import descriptor_pb2
203
204 if not hasattr(descriptor_pb2, self._options_class_name):
205 raise RuntimeError(
206 'Unknown options class name %s!' % self._options_class_name
207 )
208 options_class = getattr(descriptor_pb2, self._options_class_name)
209 features = None
210 edition = self.file._edition
211
212 if not self.has_options:
213 if not self._features:
214 features = self._ResolveFeatures(
215 descriptor_pb2.Edition.Value(edition), options_class()
216 )
217 with _lock:
218 self._options = self._loaded_options = options_class()
219 if not self._features:
220 self._features = features
221 else:
222 if not self._serialized_options:
223 options = self._original_options
224 else:
225 options = _ParseOptions(options_class(), self._serialized_options)
226
227 if not self._features:
228 features = self._ResolveFeatures(
229 descriptor_pb2.Edition.Value(edition), options
230 )
231 with _lock:
232 self._options = self._loaded_options = options
233 if not self._features:
234 self._features = features
235 if options.HasField('features'):
236 options.ClearField('features')
237 if not options.SerializeToString():
238 self._options = self._loaded_options = options_class()
239 self.has_options = False
240
241 def GetOptions(self):
242 """Retrieves descriptor options.
243
244 Returns:
245 The options set on this descriptor.
246 """
247 # If either has been reset by gencode, reload options.
248 if not self._options or not self._loaded_options:
249 self._LazyLoadOptions()
250 return self._options
251
252
253class _NestedDescriptorBase(DescriptorBase):
254 """Common class for descriptors that can be nested."""
255
256 def __init__(
257 self,
258 options,
259 options_class_name,
260 name,
261 full_name,
262 file,
263 containing_type,
264 serialized_start=None,
265 serialized_end=None,
266 serialized_options=None,
267 ):
268 """Constructor.
269
270 Args:
271 options: Protocol message options or None to use default message options.
272 options_class_name (str): The class name of the above options.
273 name (str): Name of this protocol message type.
274 full_name (str): Fully-qualified name of this protocol message type, which
275 will include protocol "package" name and the name of any enclosing
276 types.
277 containing_type: if provided, this is a nested descriptor, with this
278 descriptor as parent, otherwise None.
279 serialized_start: The start index (inclusive) in block in the
280 file.serialized_pb that describes this descriptor.
281 serialized_end: The end index (exclusive) in block in the
282 file.serialized_pb that describes this descriptor.
283 serialized_options: Protocol message serialized options or None.
284 """
285 super(_NestedDescriptorBase, self).__init__(
286 file, options, serialized_options, options_class_name
287 )
288
289 self.name = name
290 # TODO: Add function to calculate full_name instead of having it in
291 # memory?
292 self.full_name = full_name
293 self.containing_type = containing_type
294
295 self._serialized_start = serialized_start
296 self._serialized_end = serialized_end
297
298 def CopyToProto(self, proto):
299 """Copies this to the matching proto in descriptor_pb2.
300
301 Args:
302 proto: An empty proto instance from descriptor_pb2.
303
304 Raises:
305 Error: If self couldn't be serialized, due to to few constructor
306 arguments.
307 """
308 if (
309 self.file is not None
310 and self._serialized_start is not None
311 and self._serialized_end is not None
312 ):
313 proto.ParseFromString(
314 self.file.serialized_pb[self._serialized_start : self._serialized_end]
315 )
316 else:
317 raise Error('Descriptor does not contain serialization.')
318
319
320class Descriptor(_NestedDescriptorBase):
321 """Descriptor for a protocol message type.
322
323 Attributes:
324 name (str): Name of this protocol message type.
325 full_name (str): Fully-qualified name of this protocol message type, which
326 will include protocol "package" name and the name of any enclosing
327 types.
328 containing_type (Descriptor): Reference to the descriptor of the type
329 containing us, or None if this is top-level.
330 fields (list[FieldDescriptor]): Field descriptors for all fields in this
331 type.
332 fields_by_number (dict(int, FieldDescriptor)): Same
333 :class:`FieldDescriptor` objects as in :attr:`fields`, but indexed by
334 "number" attribute in each FieldDescriptor.
335 fields_by_name (dict(str, FieldDescriptor)): Same :class:`FieldDescriptor`
336 objects as in :attr:`fields`, but indexed by "name" attribute in each
337 :class:`FieldDescriptor`.
338 nested_types (list[Descriptor]): Descriptor references for all protocol
339 message types nested within this one.
340 nested_types_by_name (dict(str, Descriptor)): Same Descriptor objects as
341 in :attr:`nested_types`, but indexed by "name" attribute in each
342 Descriptor.
343 enum_types (list[EnumDescriptor]): :class:`EnumDescriptor` references for
344 all enums contained within this type.
345 enum_types_by_name (dict(str, EnumDescriptor)): Same
346 :class:`EnumDescriptor` objects as in :attr:`enum_types`, but indexed by
347 "name" attribute in each EnumDescriptor.
348 enum_values_by_name (dict(str, EnumValueDescriptor)): Dict mapping from
349 enum value name to :class:`EnumValueDescriptor` for that value.
350 extensions (list[FieldDescriptor]): All extensions defined directly within
351 this message type (NOT within a nested type).
352 extensions_by_name (dict(str, FieldDescriptor)): Same FieldDescriptor
353 objects as :attr:`extensions`, but indexed by "name" attribute of each
354 FieldDescriptor.
355 is_extendable (bool): Does this type define any extension ranges?
356 oneofs (list[OneofDescriptor]): The list of descriptors for oneof fields
357 in this message.
358 oneofs_by_name (dict(str, OneofDescriptor)): Same objects as in
359 :attr:`oneofs`, but indexed by "name" attribute.
360 file (FileDescriptor): Reference to file descriptor.
361 is_map_entry: If the message type is a map entry.
362 """
363
364 if _USE_C_DESCRIPTORS:
365 _C_DESCRIPTOR_CLASS = _message.Descriptor
366
367 def __new__(
368 cls,
369 name=None,
370 full_name=None,
371 filename=None,
372 containing_type=None,
373 fields=None,
374 nested_types=None,
375 enum_types=None,
376 extensions=None,
377 options=None,
378 serialized_options=None,
379 is_extendable=True,
380 extension_ranges=None,
381 oneofs=None,
382 file=None, # pylint: disable=redefined-builtin
383 serialized_start=None,
384 serialized_end=None,
385 syntax=None,
386 is_map_entry=False,
387 create_key=None,
388 ):
389 _message.Message._CheckCalledFromGeneratedFile()
390 return _message.default_pool.FindMessageTypeByName(full_name)
391
392 # NOTE: The file argument redefining a builtin is nothing we can
393 # fix right now since we don't know how many clients already rely on the
394 # name of the argument.
395 def __init__(
396 self,
397 name,
398 full_name,
399 filename,
400 containing_type,
401 fields,
402 nested_types,
403 enum_types,
404 extensions,
405 options=None,
406 serialized_options=None,
407 is_extendable=True,
408 extension_ranges=None,
409 oneofs=None,
410 file=None,
411 serialized_start=None,
412 serialized_end=None, # pylint: disable=redefined-builtin
413 syntax=None,
414 is_map_entry=False,
415 create_key=None,
416 ):
417 """Arguments to __init__() are as described in the description
418
419 of Descriptor fields above.
420
421 Note that filename is an obsolete argument, that is not used anymore.
422 Please use file.name to access this as an attribute.
423 """
424 if create_key is not _internal_create_key:
425 _Deprecated('create function Descriptor()')
426
427 super(Descriptor, self).__init__(
428 options,
429 'MessageOptions',
430 name,
431 full_name,
432 file,
433 containing_type,
434 serialized_start=serialized_start,
435 serialized_end=serialized_end,
436 serialized_options=serialized_options,
437 )
438
439 # We have fields in addition to fields_by_name and fields_by_number,
440 # so that:
441 # 1. Clients can index fields by "order in which they're listed."
442 # 2. Clients can easily iterate over all fields with the terse
443 # syntax: for f in descriptor.fields: ...
444 self.fields = fields
445 for field in self.fields:
446 field.containing_type = self
447 field.file = file
448 self.fields_by_number = dict((f.number, f) for f in fields)
449 self.fields_by_name = dict((f.name, f) for f in fields)
450 self._fields_by_camelcase_name = None
451
452 self.nested_types = nested_types
453 for nested_type in nested_types:
454 nested_type.containing_type = self
455 self.nested_types_by_name = dict((t.name, t) for t in nested_types)
456
457 self.enum_types = enum_types
458 for enum_type in self.enum_types:
459 enum_type.containing_type = self
460 self.enum_types_by_name = dict((t.name, t) for t in enum_types)
461 self.enum_values_by_name = dict(
462 (v.name, v) for t in enum_types for v in t.values
463 )
464
465 self.extensions = extensions
466 for extension in self.extensions:
467 extension.extension_scope = self
468 self.extensions_by_name = dict((f.name, f) for f in extensions)
469 self.is_extendable = is_extendable
470 self.extension_ranges = extension_ranges
471 self.oneofs = oneofs if oneofs is not None else []
472 self.oneofs_by_name = dict((o.name, o) for o in self.oneofs)
473 for oneof in self.oneofs:
474 oneof.containing_type = self
475 oneof.file = file
476 self._is_map_entry = is_map_entry
477
478 @property
479 def _parent(self):
480 return self.containing_type or self.file
481
482 @property
483 def fields_by_camelcase_name(self):
484 """Same FieldDescriptor objects as in :attr:`fields`, but indexed by
485
486 :attr:`FieldDescriptor.camelcase_name`.
487 """
488 if self._fields_by_camelcase_name is None:
489 self._fields_by_camelcase_name = dict(
490 (f.camelcase_name, f) for f in self.fields
491 )
492 return self._fields_by_camelcase_name
493
494 def EnumValueName(self, enum, value):
495 """Returns the string name of an enum value.
496
497 This is just a small helper method to simplify a common operation.
498
499 Args:
500 enum: string name of the Enum.
501 value: int, value of the enum.
502
503 Returns:
504 string name of the enum value.
505
506 Raises:
507 KeyError if either the Enum doesn't exist or the value is not a valid
508 value for the enum.
509 """
510 return self.enum_types_by_name[enum].values_by_number[value].name
511
512 def CopyToProto(self, proto):
513 """Copies this to a descriptor_pb2.DescriptorProto.
514
515 Args:
516 proto: An empty descriptor_pb2.DescriptorProto.
517 """
518 # This function is overridden to give a better doc comment.
519 super(Descriptor, self).CopyToProto(proto)
520
521
522# TODO: We should have aggressive checking here,
523# for example:
524# * If you specify a repeated field, you should not be allowed
525# to specify a default value.
526# * [Other examples here as needed].
527#
528# TODO: for this and other *Descriptor classes, we
529# might also want to lock things down aggressively (e.g.,
530# prevent clients from setting the attributes). Having
531# stronger invariants here in general will reduce the number
532# of runtime checks we must do in reflection.py...
533class FieldDescriptor(DescriptorBase):
534 """Descriptor for a single field in a .proto file.
535
536 Attributes:
537 name (str): Name of this field, exactly as it appears in .proto.
538 full_name (str): Name of this field, including containing scope. This is
539 particularly relevant for extensions.
540 index (int): Dense, 0-indexed index giving the order that this field
541 textually appears within its message in the .proto file.
542 number (int): Tag number declared for this field in the .proto file.
543 type (int): (One of the TYPE_* constants below) Declared type.
544 cpp_type (int): (One of the CPPTYPE_* constants below) C++ type used to
545 represent this field.
546 label (int): (One of the LABEL_* constants below) Tells whether this field
547 is optional, required, or repeated.
548 has_default_value (bool): True if this field has a default value defined,
549 otherwise false.
550 default_value (Varies): Default value of this field. Only meaningful for
551 non-repeated scalar fields. Repeated fields should always set this to [],
552 and non-repeated composite fields should always set this to None.
553 containing_type (Descriptor): Descriptor of the protocol message type that
554 contains this field. Set by the Descriptor constructor if we're passed
555 into one. Somewhat confusingly, for extension fields, this is the
556 descriptor of the EXTENDED message, not the descriptor of the message
557 containing this field. (See is_extension and extension_scope below).
558 message_type (Descriptor): If a composite field, a descriptor of the message
559 type contained in this field. Otherwise, this is None.
560 enum_type (EnumDescriptor): If this field contains an enum, a descriptor of
561 that enum. Otherwise, this is None.
562 is_extension: True iff this describes an extension field.
563 extension_scope (Descriptor): Only meaningful if is_extension is True. Gives
564 the message that immediately contains this extension field. Will be None
565 iff we're a top-level (file-level) extension field.
566 options (descriptor_pb2.FieldOptions): Protocol message field options or
567 None to use default field options.
568 containing_oneof (OneofDescriptor): If the field is a member of a oneof
569 union, contains its descriptor. Otherwise, None.
570 file (FileDescriptor): Reference to file descriptor.
571 """
572
573 # Must be consistent with C++ FieldDescriptor::Type enum in
574 # descriptor.h.
575 #
576 # TODO: Find a way to eliminate this repetition.
577 TYPE_DOUBLE = 1
578 TYPE_FLOAT = 2
579 TYPE_INT64 = 3
580 TYPE_UINT64 = 4
581 TYPE_INT32 = 5
582 TYPE_FIXED64 = 6
583 TYPE_FIXED32 = 7
584 TYPE_BOOL = 8
585 TYPE_STRING = 9
586 TYPE_GROUP = 10
587 TYPE_MESSAGE = 11
588 TYPE_BYTES = 12
589 TYPE_UINT32 = 13
590 TYPE_ENUM = 14
591 TYPE_SFIXED32 = 15
592 TYPE_SFIXED64 = 16
593 TYPE_SINT32 = 17
594 TYPE_SINT64 = 18
595 MAX_TYPE = 18
596
597 # Must be consistent with C++ FieldDescriptor::CppType enum in
598 # descriptor.h.
599 #
600 # TODO: Find a way to eliminate this repetition.
601 CPPTYPE_INT32 = 1
602 CPPTYPE_INT64 = 2
603 CPPTYPE_UINT32 = 3
604 CPPTYPE_UINT64 = 4
605 CPPTYPE_DOUBLE = 5
606 CPPTYPE_FLOAT = 6
607 CPPTYPE_BOOL = 7
608 CPPTYPE_ENUM = 8
609 CPPTYPE_STRING = 9
610 CPPTYPE_MESSAGE = 10
611 MAX_CPPTYPE = 10
612
613 _PYTHON_TO_CPP_PROTO_TYPE_MAP = {
614 TYPE_DOUBLE: CPPTYPE_DOUBLE,
615 TYPE_FLOAT: CPPTYPE_FLOAT,
616 TYPE_ENUM: CPPTYPE_ENUM,
617 TYPE_INT64: CPPTYPE_INT64,
618 TYPE_SINT64: CPPTYPE_INT64,
619 TYPE_SFIXED64: CPPTYPE_INT64,
620 TYPE_UINT64: CPPTYPE_UINT64,
621 TYPE_FIXED64: CPPTYPE_UINT64,
622 TYPE_INT32: CPPTYPE_INT32,
623 TYPE_SFIXED32: CPPTYPE_INT32,
624 TYPE_SINT32: CPPTYPE_INT32,
625 TYPE_UINT32: CPPTYPE_UINT32,
626 TYPE_FIXED32: CPPTYPE_UINT32,
627 TYPE_BYTES: CPPTYPE_STRING,
628 TYPE_STRING: CPPTYPE_STRING,
629 TYPE_BOOL: CPPTYPE_BOOL,
630 TYPE_MESSAGE: CPPTYPE_MESSAGE,
631 TYPE_GROUP: CPPTYPE_MESSAGE,
632 }
633
634 # Must be consistent with C++ FieldDescriptor::Label enum in
635 # descriptor.h.
636 #
637 # TODO: Find a way to eliminate this repetition.
638 LABEL_OPTIONAL = 1
639 LABEL_REQUIRED = 2
640 LABEL_REPEATED = 3
641 MAX_LABEL = 3
642
643 # Must be consistent with C++ constants kMaxNumber, kFirstReservedNumber,
644 # and kLastReservedNumber in descriptor.h
645 MAX_FIELD_NUMBER = (1 << 29) - 1
646 FIRST_RESERVED_FIELD_NUMBER = 19000
647 LAST_RESERVED_FIELD_NUMBER = 19999
648
649 if _USE_C_DESCRIPTORS:
650 _C_DESCRIPTOR_CLASS = _message.FieldDescriptor
651
652 def __new__(
653 cls,
654 name,
655 full_name,
656 index,
657 number,
658 type,
659 cpp_type,
660 label,
661 default_value,
662 message_type,
663 enum_type,
664 containing_type,
665 is_extension,
666 extension_scope,
667 options=None,
668 serialized_options=None,
669 has_default_value=True,
670 containing_oneof=None,
671 json_name=None,
672 file=None,
673 create_key=None,
674 ): # pylint: disable=redefined-builtin
675 _message.Message._CheckCalledFromGeneratedFile()
676 if is_extension:
677 return _message.default_pool.FindExtensionByName(full_name)
678 else:
679 return _message.default_pool.FindFieldByName(full_name)
680
681 def __init__(
682 self,
683 name,
684 full_name,
685 index,
686 number,
687 type,
688 cpp_type,
689 label,
690 default_value,
691 message_type,
692 enum_type,
693 containing_type,
694 is_extension,
695 extension_scope,
696 options=None,
697 serialized_options=None,
698 has_default_value=True,
699 containing_oneof=None,
700 json_name=None,
701 file=None,
702 create_key=None,
703 ): # pylint: disable=redefined-builtin
704 """The arguments are as described in the description of FieldDescriptor
705
706 attributes above.
707
708 Note that containing_type may be None, and may be set later if necessary
709 (to deal with circular references between message types, for example).
710 Likewise for extension_scope.
711 """
712 if create_key is not _internal_create_key:
713 _Deprecated('create function FieldDescriptor()')
714
715 super(FieldDescriptor, self).__init__(
716 file, options, serialized_options, 'FieldOptions'
717 )
718 self.name = name
719 self.full_name = full_name
720 self._camelcase_name = None
721 if json_name is None:
722 self.json_name = _ToJsonName(name)
723 else:
724 self.json_name = json_name
725 self.index = index
726 self.number = number
727 self._type = type
728 self.cpp_type = cpp_type
729 self._label = label
730 self.has_default_value = has_default_value
731 self.default_value = default_value
732 self.containing_type = containing_type
733 self.message_type = message_type
734 self.enum_type = enum_type
735 self.is_extension = is_extension
736 self.extension_scope = extension_scope
737 self.containing_oneof = containing_oneof
738 if api_implementation.Type() == 'python':
739 self._cdescriptor = None
740 else:
741 if is_extension:
742 self._cdescriptor = _message.default_pool.FindExtensionByName(full_name)
743 else:
744 self._cdescriptor = _message.default_pool.FindFieldByName(full_name)
745
746 @property
747 def _parent(self):
748 if self.containing_oneof:
749 return self.containing_oneof
750 if self.is_extension:
751 return self.extension_scope or self.file
752 return self.containing_type
753
754 def _InferLegacyFeatures(self, edition, options, features):
755 # pylint: disable=g-import-not-at-top
756 from google.protobuf import descriptor_pb2
757
758 if edition >= descriptor_pb2.Edition.EDITION_2023:
759 return
760
761 if self._label == FieldDescriptor.LABEL_REQUIRED:
762 features.field_presence = (
763 descriptor_pb2.FeatureSet.FieldPresence.LEGACY_REQUIRED
764 )
765
766 if self._type == FieldDescriptor.TYPE_GROUP:
767 features.message_encoding = (
768 descriptor_pb2.FeatureSet.MessageEncoding.DELIMITED
769 )
770
771 if options.HasField('packed'):
772 features.repeated_field_encoding = (
773 descriptor_pb2.FeatureSet.RepeatedFieldEncoding.PACKED
774 if options.packed
775 else descriptor_pb2.FeatureSet.RepeatedFieldEncoding.EXPANDED
776 )
777
778 @property
779 def type(self):
780 if (
781 self._GetFeatures().message_encoding
782 == _FEATURESET_MESSAGE_ENCODING_DELIMITED
783 and self.message_type
784 and not self.message_type.GetOptions().map_entry
785 and not self.containing_type.GetOptions().map_entry
786 ):
787 return FieldDescriptor.TYPE_GROUP
788 return self._type
789
790 @type.setter
791 def type(self, val):
792 self._type = val
793
794 @property
795 def label(self):
796 _Deprecated('label property', 'is_required or is_repeated properties')
797
798 if (
799 self._GetFeatures().field_presence
800 == _FEATURESET_FIELD_PRESENCE_LEGACY_REQUIRED
801 ):
802 return FieldDescriptor.LABEL_REQUIRED
803 return self._label
804
805 @property
806 def is_required(self):
807 """Returns if the field is required."""
808 return (
809 self._GetFeatures().field_presence
810 == _FEATURESET_FIELD_PRESENCE_LEGACY_REQUIRED
811 )
812
813 @property
814 def is_repeated(self):
815 """Returns if the field is repeated."""
816 return self._label == FieldDescriptor.LABEL_REPEATED
817
818 @property
819 def camelcase_name(self):
820 """Camelcase name of this field.
821
822 Returns:
823 str: the name in CamelCase.
824 """
825 if self._camelcase_name is None:
826 self._camelcase_name = _ToCamelCase(self.name)
827 return self._camelcase_name
828
829 @property
830 def has_presence(self):
831 """Whether the field distinguishes between unpopulated and default values.
832
833 Raises:
834 RuntimeError: singular field that is not linked with message nor file.
835 """
836 if self.is_repeated:
837 return False
838 if (
839 self.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE
840 or self.is_extension
841 or self.containing_oneof
842 ):
843 return True
844
845 return (
846 self._GetFeatures().field_presence
847 != _FEATURESET_FIELD_PRESENCE_IMPLICIT
848 )
849
850 @property
851 def is_packed(self):
852 """Returns if the field is packed."""
853 if not self.is_repeated:
854 return False
855 field_type = self.type
856 if (
857 field_type == FieldDescriptor.TYPE_STRING
858 or field_type == FieldDescriptor.TYPE_GROUP
859 or field_type == FieldDescriptor.TYPE_MESSAGE
860 or field_type == FieldDescriptor.TYPE_BYTES
861 ):
862 return False
863
864 return (
865 self._GetFeatures().repeated_field_encoding
866 == _FEATURESET_REPEATED_FIELD_ENCODING_PACKED
867 )
868
869 @staticmethod
870 def ProtoTypeToCppProtoType(proto_type):
871 """Converts from a Python proto type to a C++ Proto Type.
872
873 The Python ProtocolBuffer classes specify both the 'Python' datatype and the
874 'C++' datatype - and they're not the same. This helper method should
875 translate from one to another.
876
877 Args:
878 proto_type: the Python proto type (descriptor.FieldDescriptor.TYPE_*)
879
880 Returns:
881 int: descriptor.FieldDescriptor.CPPTYPE_*, the C++ type.
882 Raises:
883 TypeTransformationError: when the Python proto type isn't known.
884 """
885 try:
886 return FieldDescriptor._PYTHON_TO_CPP_PROTO_TYPE_MAP[proto_type]
887 except KeyError:
888 raise TypeTransformationError('Unknown proto_type: %s' % proto_type)
889
890
891class EnumDescriptor(_NestedDescriptorBase):
892 """Descriptor for an enum defined in a .proto file.
893
894 Attributes:
895 name (str): Name of the enum type.
896 full_name (str): Full name of the type, including package name and any
897 enclosing type(s).
898 values (list[EnumValueDescriptor]): List of the values in this enum.
899 values_by_name (dict(str, EnumValueDescriptor)): Same as :attr:`values`, but
900 indexed by the "name" field of each EnumValueDescriptor.
901 values_by_number (dict(int, EnumValueDescriptor)): Same as :attr:`values`,
902 but indexed by the "number" field of each EnumValueDescriptor.
903 containing_type (Descriptor): Descriptor of the immediate containing type of
904 this enum, or None if this is an enum defined at the top level in a .proto
905 file. Set by Descriptor's constructor if we're passed into one.
906 file (FileDescriptor): Reference to file descriptor.
907 options (descriptor_pb2.EnumOptions): Enum options message or None to use
908 default enum options.
909 """
910
911 if _USE_C_DESCRIPTORS:
912 _C_DESCRIPTOR_CLASS = _message.EnumDescriptor
913
914 def __new__(
915 cls,
916 name,
917 full_name,
918 filename,
919 values,
920 containing_type=None,
921 options=None,
922 serialized_options=None,
923 file=None, # pylint: disable=redefined-builtin
924 serialized_start=None,
925 serialized_end=None,
926 create_key=None,
927 ):
928 _message.Message._CheckCalledFromGeneratedFile()
929 return _message.default_pool.FindEnumTypeByName(full_name)
930
931 def __init__(
932 self,
933 name,
934 full_name,
935 filename,
936 values,
937 containing_type=None,
938 options=None,
939 serialized_options=None,
940 file=None, # pylint: disable=redefined-builtin
941 serialized_start=None,
942 serialized_end=None,
943 create_key=None,
944 ):
945 """Arguments are as described in the attribute description above.
946
947 Note that filename is an obsolete argument, that is not used anymore.
948 Please use file.name to access this as an attribute.
949 """
950 if create_key is not _internal_create_key:
951 _Deprecated('create function EnumDescriptor()')
952
953 super(EnumDescriptor, self).__init__(
954 options,
955 'EnumOptions',
956 name,
957 full_name,
958 file,
959 containing_type,
960 serialized_start=serialized_start,
961 serialized_end=serialized_end,
962 serialized_options=serialized_options,
963 )
964
965 self.values = values
966 for value in self.values:
967 value.file = file
968 value.type = self
969 self.values_by_name = dict((v.name, v) for v in values)
970 # Values are reversed to ensure that the first alias is retained.
971 self.values_by_number = dict((v.number, v) for v in reversed(values))
972
973 @property
974 def _parent(self):
975 return self.containing_type or self.file
976
977 @property
978 def is_closed(self):
979 """Returns true whether this is a "closed" enum.
980
981 This means that it:
982 - Has a fixed set of values, rather than being equivalent to an int32.
983 - Encountering values not in this set causes them to be treated as unknown
984 fields.
985 - The first value (i.e., the default) may be nonzero.
986
987 WARNING: Some runtimes currently have a quirk where non-closed enums are
988 treated as closed when used as the type of fields defined in a
989 `syntax = proto2;` file. This quirk is not present in all runtimes; as of
990 writing, we know that:
991
992 - C++, Java, and C++-based Python share this quirk.
993 - UPB and UPB-based Python do not.
994 - PHP and Ruby treat all enums as open regardless of declaration.
995
996 Care should be taken when using this function to respect the target
997 runtime's enum handling quirks.
998 """
999 return self._GetFeatures().enum_type == _FEATURESET_ENUM_TYPE_CLOSED
1000
1001 def CopyToProto(self, proto):
1002 """Copies this to a descriptor_pb2.EnumDescriptorProto.
1003
1004 Args:
1005 proto (descriptor_pb2.EnumDescriptorProto): An empty descriptor proto.
1006 """
1007 # This function is overridden to give a better doc comment.
1008 super(EnumDescriptor, self).CopyToProto(proto)
1009
1010
1011class EnumValueDescriptor(DescriptorBase):
1012 """Descriptor for a single value within an enum.
1013
1014 Attributes:
1015 name (str): Name of this value.
1016 index (int): Dense, 0-indexed index giving the order that this value appears
1017 textually within its enum in the .proto file.
1018 number (int): Actual number assigned to this enum value.
1019 type (EnumDescriptor): :class:`EnumDescriptor` to which this value belongs.
1020 Set by :class:`EnumDescriptor`'s constructor if we're passed into one.
1021 options (descriptor_pb2.EnumValueOptions): Enum value options message or
1022 None to use default enum value options options.
1023 """
1024
1025 if _USE_C_DESCRIPTORS:
1026 _C_DESCRIPTOR_CLASS = _message.EnumValueDescriptor
1027
1028 def __new__(
1029 cls,
1030 name,
1031 index,
1032 number,
1033 type=None, # pylint: disable=redefined-builtin
1034 options=None,
1035 serialized_options=None,
1036 create_key=None,
1037 ):
1038 _message.Message._CheckCalledFromGeneratedFile()
1039 # There is no way we can build a complete EnumValueDescriptor with the
1040 # given parameters (the name of the Enum is not known, for example).
1041 # Fortunately generated files just pass it to the EnumDescriptor()
1042 # constructor, which will ignore it, so returning None is good enough.
1043 return None
1044
1045 def __init__(
1046 self,
1047 name,
1048 index,
1049 number,
1050 type=None, # pylint: disable=redefined-builtin
1051 options=None,
1052 serialized_options=None,
1053 create_key=None,
1054 ):
1055 """Arguments are as described in the attribute description above."""
1056 if create_key is not _internal_create_key:
1057 _Deprecated('create function EnumValueDescriptor()')
1058
1059 super(EnumValueDescriptor, self).__init__(
1060 type.file if type else None,
1061 options,
1062 serialized_options,
1063 'EnumValueOptions',
1064 )
1065 self.name = name
1066 self.index = index
1067 self.number = number
1068 self.type = type
1069
1070 @property
1071 def _parent(self):
1072 return self.type
1073
1074
1075class OneofDescriptor(DescriptorBase):
1076 """Descriptor for a oneof field.
1077
1078 Attributes:
1079 name (str): Name of the oneof field.
1080 full_name (str): Full name of the oneof field, including package name.
1081 index (int): 0-based index giving the order of the oneof field inside its
1082 containing type.
1083 containing_type (Descriptor): :class:`Descriptor` of the protocol message
1084 type that contains this field. Set by the :class:`Descriptor` constructor
1085 if we're passed into one.
1086 fields (list[FieldDescriptor]): The list of field descriptors this oneof can
1087 contain.
1088 """
1089
1090 if _USE_C_DESCRIPTORS:
1091 _C_DESCRIPTOR_CLASS = _message.OneofDescriptor
1092
1093 def __new__(
1094 cls,
1095 name,
1096 full_name,
1097 index,
1098 containing_type,
1099 fields,
1100 options=None,
1101 serialized_options=None,
1102 create_key=None,
1103 ):
1104 _message.Message._CheckCalledFromGeneratedFile()
1105 return _message.default_pool.FindOneofByName(full_name)
1106
1107 def __init__(
1108 self,
1109 name,
1110 full_name,
1111 index,
1112 containing_type,
1113 fields,
1114 options=None,
1115 serialized_options=None,
1116 create_key=None,
1117 ):
1118 """Arguments are as described in the attribute description above."""
1119 if create_key is not _internal_create_key:
1120 _Deprecated('create function OneofDescriptor()')
1121
1122 super(OneofDescriptor, self).__init__(
1123 containing_type.file if containing_type else None,
1124 options,
1125 serialized_options,
1126 'OneofOptions',
1127 )
1128 self.name = name
1129 self.full_name = full_name
1130 self.index = index
1131 self.containing_type = containing_type
1132 self.fields = fields
1133
1134 @property
1135 def _parent(self):
1136 return self.containing_type
1137
1138
1139class ServiceDescriptor(_NestedDescriptorBase):
1140 """Descriptor for a service.
1141
1142 Attributes:
1143 name (str): Name of the service.
1144 full_name (str): Full name of the service, including package name.
1145 index (int): 0-indexed index giving the order that this services definition
1146 appears within the .proto file.
1147 methods (list[MethodDescriptor]): List of methods provided by this service.
1148 methods_by_name (dict(str, MethodDescriptor)): Same
1149 :class:`MethodDescriptor` objects as in :attr:`methods_by_name`, but
1150 indexed by "name" attribute in each :class:`MethodDescriptor`.
1151 options (descriptor_pb2.ServiceOptions): Service options message or None to
1152 use default service options.
1153 file (FileDescriptor): Reference to file info.
1154 """
1155
1156 if _USE_C_DESCRIPTORS:
1157 _C_DESCRIPTOR_CLASS = _message.ServiceDescriptor
1158
1159 def __new__(
1160 cls,
1161 name=None,
1162 full_name=None,
1163 index=None,
1164 methods=None,
1165 options=None,
1166 serialized_options=None,
1167 file=None, # pylint: disable=redefined-builtin
1168 serialized_start=None,
1169 serialized_end=None,
1170 create_key=None,
1171 ):
1172 _message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access
1173 return _message.default_pool.FindServiceByName(full_name)
1174
1175 def __init__(
1176 self,
1177 name,
1178 full_name,
1179 index,
1180 methods,
1181 options=None,
1182 serialized_options=None,
1183 file=None, # pylint: disable=redefined-builtin
1184 serialized_start=None,
1185 serialized_end=None,
1186 create_key=None,
1187 ):
1188 if create_key is not _internal_create_key:
1189 _Deprecated('create function ServiceDescriptor()')
1190
1191 super(ServiceDescriptor, self).__init__(
1192 options,
1193 'ServiceOptions',
1194 name,
1195 full_name,
1196 file,
1197 None,
1198 serialized_start=serialized_start,
1199 serialized_end=serialized_end,
1200 serialized_options=serialized_options,
1201 )
1202 self.index = index
1203 self.methods = methods
1204 self.methods_by_name = dict((m.name, m) for m in methods)
1205 # Set the containing service for each method in this service.
1206 for method in self.methods:
1207 method.file = self.file
1208 method.containing_service = self
1209
1210 @property
1211 def _parent(self):
1212 return self.file
1213
1214 def FindMethodByName(self, name):
1215 """Searches for the specified method, and returns its descriptor.
1216
1217 Args:
1218 name (str): Name of the method.
1219
1220 Returns:
1221 MethodDescriptor: The descriptor for the requested method.
1222
1223 Raises:
1224 KeyError: if the method cannot be found in the service.
1225 """
1226 return self.methods_by_name[name]
1227
1228 def CopyToProto(self, proto):
1229 """Copies this to a descriptor_pb2.ServiceDescriptorProto.
1230
1231 Args:
1232 proto (descriptor_pb2.ServiceDescriptorProto): An empty descriptor proto.
1233 """
1234 # This function is overridden to give a better doc comment.
1235 super(ServiceDescriptor, self).CopyToProto(proto)
1236
1237
1238class MethodDescriptor(DescriptorBase):
1239 """Descriptor for a method in a service.
1240
1241 Attributes:
1242 name (str): Name of the method within the service.
1243 full_name (str): Full name of method.
1244 index (int): 0-indexed index of the method inside the service.
1245 containing_service (ServiceDescriptor): The service that contains this
1246 method.
1247 input_type (Descriptor): The descriptor of the message that this method
1248 accepts.
1249 output_type (Descriptor): The descriptor of the message that this method
1250 returns.
1251 client_streaming (bool): Whether this method uses client streaming.
1252 server_streaming (bool): Whether this method uses server streaming.
1253 options (descriptor_pb2.MethodOptions or None): Method options message, or
1254 None to use default method options.
1255 """
1256
1257 if _USE_C_DESCRIPTORS:
1258 _C_DESCRIPTOR_CLASS = _message.MethodDescriptor
1259
1260 def __new__(
1261 cls,
1262 name,
1263 full_name,
1264 index,
1265 containing_service,
1266 input_type,
1267 output_type,
1268 client_streaming=False,
1269 server_streaming=False,
1270 options=None,
1271 serialized_options=None,
1272 create_key=None,
1273 ):
1274 _message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access
1275 return _message.default_pool.FindMethodByName(full_name)
1276
1277 def __init__(
1278 self,
1279 name,
1280 full_name,
1281 index,
1282 containing_service,
1283 input_type,
1284 output_type,
1285 client_streaming=False,
1286 server_streaming=False,
1287 options=None,
1288 serialized_options=None,
1289 create_key=None,
1290 ):
1291 """The arguments are as described in the description of MethodDescriptor
1292
1293 attributes above.
1294
1295 Note that containing_service may be None, and may be set later if necessary.
1296 """
1297 if create_key is not _internal_create_key:
1298 _Deprecated('create function MethodDescriptor()')
1299
1300 super(MethodDescriptor, self).__init__(
1301 containing_service.file if containing_service else None,
1302 options,
1303 serialized_options,
1304 'MethodOptions',
1305 )
1306 self.name = name
1307 self.full_name = full_name
1308 self.index = index
1309 self.containing_service = containing_service
1310 self.input_type = input_type
1311 self.output_type = output_type
1312 self.client_streaming = client_streaming
1313 self.server_streaming = server_streaming
1314
1315 @property
1316 def _parent(self):
1317 return self.containing_service
1318
1319 def CopyToProto(self, proto):
1320 """Copies this to a descriptor_pb2.MethodDescriptorProto.
1321
1322 Args:
1323 proto (descriptor_pb2.MethodDescriptorProto): An empty descriptor proto.
1324
1325 Raises:
1326 Error: If self couldn't be serialized, due to too few constructor
1327 arguments.
1328 """
1329 if self.containing_service is not None:
1330 from google.protobuf import descriptor_pb2
1331
1332 service_proto = descriptor_pb2.ServiceDescriptorProto()
1333 self.containing_service.CopyToProto(service_proto)
1334 proto.CopyFrom(service_proto.method[self.index])
1335 else:
1336 raise Error('Descriptor does not contain a service.')
1337
1338
1339class FileDescriptor(DescriptorBase):
1340 """Descriptor for a file. Mimics the descriptor_pb2.FileDescriptorProto.
1341
1342 Note that :attr:`enum_types_by_name`, :attr:`extensions_by_name`, and
1343 :attr:`dependencies` fields are only set by the
1344 :py:mod:`google.protobuf.message_factory` module, and not by the generated
1345 proto code.
1346
1347 Attributes:
1348 name (str): Name of file, relative to root of source tree.
1349 package (str): Name of the package
1350 edition (Edition): Enum value indicating edition of the file
1351 serialized_pb (bytes): Byte string of serialized
1352 :class:`descriptor_pb2.FileDescriptorProto`.
1353 dependencies (list[FileDescriptor]): List of other :class:`FileDescriptor`
1354 objects this :class:`FileDescriptor` depends on.
1355 public_dependencies (list[FileDescriptor]): A subset of
1356 :attr:`dependencies`, which were declared as "public".
1357 message_types_by_name (dict(str, Descriptor)): Mapping from message names to
1358 their :class:`Descriptor`.
1359 enum_types_by_name (dict(str, EnumDescriptor)): Mapping from enum names to
1360 their :class:`EnumDescriptor`.
1361 extensions_by_name (dict(str, FieldDescriptor)): Mapping from extension
1362 names declared at file scope to their :class:`FieldDescriptor`.
1363 services_by_name (dict(str, ServiceDescriptor)): Mapping from services'
1364 names to their :class:`ServiceDescriptor`.
1365 pool (DescriptorPool): The pool this descriptor belongs to. When not passed
1366 to the constructor, the global default pool is used.
1367 """
1368
1369 if _USE_C_DESCRIPTORS:
1370 _C_DESCRIPTOR_CLASS = _message.FileDescriptor
1371
1372 def __new__(
1373 cls,
1374 name,
1375 package,
1376 options=None,
1377 serialized_options=None,
1378 serialized_pb=None,
1379 dependencies=None,
1380 public_dependencies=None,
1381 syntax=None,
1382 edition=None,
1383 pool=None,
1384 create_key=None,
1385 ):
1386 # FileDescriptor() is called from various places, not only from generated
1387 # files, to register dynamic proto files and messages.
1388 # pylint: disable=g-explicit-bool-comparison
1389 if serialized_pb:
1390 return _message.default_pool.AddSerializedFile(serialized_pb)
1391 else:
1392 return super(FileDescriptor, cls).__new__(cls)
1393
1394 def __init__(
1395 self,
1396 name,
1397 package,
1398 options=None,
1399 serialized_options=None,
1400 serialized_pb=None,
1401 dependencies=None,
1402 public_dependencies=None,
1403 syntax=None,
1404 edition=None,
1405 pool=None,
1406 create_key=None,
1407 ):
1408 """Constructor."""
1409 if create_key is not _internal_create_key:
1410 _Deprecated('create function FileDescriptor()')
1411
1412 super(FileDescriptor, self).__init__(
1413 self, options, serialized_options, 'FileOptions'
1414 )
1415
1416 if edition and edition != 'EDITION_UNKNOWN':
1417 self._edition = edition
1418 elif syntax == 'proto3':
1419 self._edition = 'EDITION_PROTO3'
1420 else:
1421 self._edition = 'EDITION_PROTO2'
1422
1423 if pool is None:
1424 from google.protobuf import descriptor_pool
1425
1426 pool = descriptor_pool.Default()
1427 self.pool = pool
1428 self.message_types_by_name = {}
1429 self.name = name
1430 self.package = package
1431 self.serialized_pb = serialized_pb
1432
1433 self.enum_types_by_name = {}
1434 self.extensions_by_name = {}
1435 self.services_by_name = {}
1436 self.dependencies = dependencies or []
1437 self.public_dependencies = public_dependencies or []
1438
1439 def CopyToProto(self, proto):
1440 """Copies this to a descriptor_pb2.FileDescriptorProto.
1441
1442 Args:
1443 proto: An empty descriptor_pb2.FileDescriptorProto.
1444 """
1445 proto.ParseFromString(self.serialized_pb)
1446
1447 @property
1448 def _parent(self):
1449 return None
1450
1451
1452def _ParseOptions(message, string):
1453 """Parses serialized options.
1454
1455 This helper function is used to parse serialized options in generated
1456 proto2 files. It must not be used outside proto2.
1457 """
1458 message.ParseFromString(string)
1459 return message
1460
1461
1462def _ToCamelCase(name):
1463 """Converts name to camel-case and returns it."""
1464 capitalize_next = False
1465 result = []
1466
1467 for c in name:
1468 if c == '_':
1469 if result:
1470 capitalize_next = True
1471 elif capitalize_next:
1472 result.append(c.upper())
1473 capitalize_next = False
1474 else:
1475 result += c
1476
1477 # Lower-case the first letter.
1478 if result and result[0].isupper():
1479 result[0] = result[0].lower()
1480 return ''.join(result)
1481
1482
1483def _OptionsOrNone(descriptor_proto):
1484 """Returns the value of the field `options`, or None if it is not set."""
1485 if descriptor_proto.HasField('options'):
1486 return descriptor_proto.options
1487 else:
1488 return None
1489
1490
1491def _ToJsonName(name):
1492 """Converts name to Json name and returns it."""
1493 capitalize_next = False
1494 result = []
1495
1496 for c in name:
1497 if c == '_':
1498 capitalize_next = True
1499 elif capitalize_next:
1500 result.append(c.upper())
1501 capitalize_next = False
1502 else:
1503 result += c
1504
1505 return ''.join(result)
1506
1507
1508def MakeDescriptor(
1509 desc_proto,
1510 package='',
1511 build_file_if_cpp=True,
1512 syntax=None,
1513 edition=None,
1514 file_desc=None,
1515):
1516 """Make a protobuf Descriptor given a DescriptorProto protobuf.
1517
1518 Handles nested descriptors. Note that this is limited to the scope of defining
1519 a message inside of another message. Composite fields can currently only be
1520 resolved if the message is defined in the same scope as the field.
1521
1522 Args:
1523 desc_proto: The descriptor_pb2.DescriptorProto protobuf message.
1524 package: Optional package name for the new message Descriptor (string).
1525 build_file_if_cpp: Update the C++ descriptor pool if api matches. Set to
1526 False on recursion, so no duplicates are created.
1527 syntax: The syntax/semantics that should be used. Set to "proto3" to get
1528 proto3 field presence semantics.
1529 edition: The edition that should be used if syntax is "edition".
1530 file_desc: A FileDescriptor to place this descriptor into.
1531
1532 Returns:
1533 A Descriptor for protobuf messages.
1534 """
1535 # pylint: disable=g-import-not-at-top
1536 from google.protobuf import descriptor_pb2
1537
1538 # Generate a random name for this proto file to prevent conflicts with any
1539 # imported ones. We need to specify a file name so the descriptor pool
1540 # accepts our FileDescriptorProto, but it is not important what that file
1541 # name is actually set to.
1542 proto_name = binascii.hexlify(os.urandom(16)).decode('ascii')
1543
1544 if package:
1545 file_name = os.path.join(package.replace('.', '/'), proto_name + '.proto')
1546 else:
1547 file_name = proto_name + '.proto'
1548
1549 if api_implementation.Type() != 'python' and build_file_if_cpp:
1550 # The C++ implementation requires all descriptors to be backed by the same
1551 # definition in the C++ descriptor pool. To do this, we build a
1552 # FileDescriptorProto with the same definition as this descriptor and build
1553 # it into the pool.
1554 file_descriptor_proto = descriptor_pb2.FileDescriptorProto()
1555 file_descriptor_proto.message_type.add().MergeFrom(desc_proto)
1556
1557 if package:
1558 file_descriptor_proto.package = package
1559 file_descriptor_proto.name = file_name
1560
1561 _message.default_pool.Add(file_descriptor_proto)
1562 result = _message.default_pool.FindFileByName(file_descriptor_proto.name)
1563
1564 if _USE_C_DESCRIPTORS:
1565 return result.message_types_by_name[desc_proto.name]
1566
1567 if file_desc is None:
1568 file_desc = FileDescriptor(
1569 pool=None,
1570 name=file_name,
1571 package=package,
1572 syntax=syntax,
1573 edition=edition,
1574 options=None,
1575 serialized_pb='',
1576 dependencies=[],
1577 public_dependencies=[],
1578 create_key=_internal_create_key,
1579 )
1580 full_message_name = [desc_proto.name]
1581 if package:
1582 full_message_name.insert(0, package)
1583
1584 # Create Descriptors for enum types
1585 enum_types = {}
1586 for enum_proto in desc_proto.enum_type:
1587 full_name = '.'.join(full_message_name + [enum_proto.name])
1588 enum_desc = EnumDescriptor(
1589 enum_proto.name,
1590 full_name,
1591 None,
1592 [
1593 EnumValueDescriptor(
1594 enum_val.name,
1595 ii,
1596 enum_val.number,
1597 create_key=_internal_create_key,
1598 )
1599 for ii, enum_val in enumerate(enum_proto.value)
1600 ],
1601 file=file_desc,
1602 create_key=_internal_create_key,
1603 )
1604 enum_types[full_name] = enum_desc
1605
1606 # Create Descriptors for nested types
1607 nested_types = {}
1608 for nested_proto in desc_proto.nested_type:
1609 full_name = '.'.join(full_message_name + [nested_proto.name])
1610 # Nested types are just those defined inside of the message, not all types
1611 # used by fields in the message, so no loops are possible here.
1612 nested_desc = MakeDescriptor(
1613 nested_proto,
1614 package='.'.join(full_message_name),
1615 build_file_if_cpp=False,
1616 syntax=syntax,
1617 edition=edition,
1618 file_desc=file_desc,
1619 )
1620 nested_types[full_name] = nested_desc
1621
1622 fields = []
1623 for field_proto in desc_proto.field:
1624 full_name = '.'.join(full_message_name + [field_proto.name])
1625 enum_desc = None
1626 nested_desc = None
1627 if field_proto.json_name:
1628 json_name = field_proto.json_name
1629 else:
1630 json_name = None
1631 if field_proto.HasField('type_name'):
1632 type_name = field_proto.type_name
1633 full_type_name = '.'.join(
1634 full_message_name + [type_name[type_name.rfind('.') + 1 :]]
1635 )
1636 if full_type_name in nested_types:
1637 nested_desc = nested_types[full_type_name]
1638 elif full_type_name in enum_types:
1639 enum_desc = enum_types[full_type_name]
1640 # Else type_name references a non-local type, which isn't implemented
1641 field = FieldDescriptor(
1642 field_proto.name,
1643 full_name,
1644 field_proto.number - 1,
1645 field_proto.number,
1646 field_proto.type,
1647 FieldDescriptor.ProtoTypeToCppProtoType(field_proto.type),
1648 field_proto.label,
1649 None,
1650 nested_desc,
1651 enum_desc,
1652 None,
1653 False,
1654 None,
1655 options=_OptionsOrNone(field_proto),
1656 has_default_value=False,
1657 json_name=json_name,
1658 file=file_desc,
1659 create_key=_internal_create_key,
1660 )
1661 fields.append(field)
1662
1663 desc_name = '.'.join(full_message_name)
1664 return Descriptor(
1665 desc_proto.name,
1666 desc_name,
1667 None,
1668 None,
1669 fields,
1670 list(nested_types.values()),
1671 list(enum_types.values()),
1672 [],
1673 options=_OptionsOrNone(desc_proto),
1674 file=file_desc,
1675 create_key=_internal_create_key,
1676 )