Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/protobuf/descriptor_pool.py: 2%
500 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-01-31 06:32 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-01-31 06:32 +0000
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
8"""Provides DescriptorPool to use as a container for proto2 descriptors.
10The DescriptorPool is used in conjection with a DescriptorDatabase to maintain
11a collection of protocol buffer descriptors for use when dynamically creating
12message types at runtime.
14For most applications protocol buffers should be used via modules generated by
15the protocol buffer compiler tool. This should only be used when the type of
16protocol buffers used in an application or library cannot be predetermined.
18Below is a straightforward example on how to use this class::
20 pool = DescriptorPool()
21 file_descriptor_protos = [ ... ]
22 for file_descriptor_proto in file_descriptor_protos:
23 pool.Add(file_descriptor_proto)
24 my_message_descriptor = pool.FindMessageTypeByName('some.package.MessageType')
26The message descriptor can be used in conjunction with the message_factory
27module in order to create a protocol buffer class that can be encoded and
28decoded.
30If you want to get a Python class for the specified proto, use the
31helper functions inside google.protobuf.message_factory
32directly instead of this class.
33"""
35__author__ = 'matthewtoia@google.com (Matt Toia)'
37import collections
38import threading
39import warnings
41from google.protobuf import descriptor
42from google.protobuf import descriptor_database
43from google.protobuf import text_encoding
44from google.protobuf.internal import python_edition_defaults
45from google.protobuf.internal import python_message
47_USE_C_DESCRIPTORS = descriptor._USE_C_DESCRIPTORS # pylint: disable=protected-access
50def _NormalizeFullyQualifiedName(name):
51 """Remove leading period from fully-qualified type name.
53 Due to b/13860351 in descriptor_database.py, types in the root namespace are
54 generated with a leading period. This function removes that prefix.
56 Args:
57 name (str): The fully-qualified symbol name.
59 Returns:
60 str: The normalized fully-qualified symbol name.
61 """
62 return name.lstrip('.')
65def _OptionsOrNone(descriptor_proto):
66 """Returns the value of the field `options`, or None if it is not set."""
67 if descriptor_proto.HasField('options'):
68 return descriptor_proto.options
69 else:
70 return None
73def _IsMessageSetExtension(field):
74 return (field.is_extension and
75 field.containing_type.has_options and
76 field.containing_type.GetOptions().message_set_wire_format and
77 field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
78 field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL)
80_edition_defaults_lock = threading.Lock()
83class DescriptorPool(object):
84 """A collection of protobufs dynamically constructed by descriptor protos."""
86 if _USE_C_DESCRIPTORS:
88 def __new__(cls, descriptor_db=None):
89 # pylint: disable=protected-access
90 return descriptor._message.DescriptorPool(descriptor_db)
92 def __init__(
93 self, descriptor_db=None, use_deprecated_legacy_json_field_conflicts=False
94 ):
95 """Initializes a Pool of proto buffs.
97 The descriptor_db argument to the constructor is provided to allow
98 specialized file descriptor proto lookup code to be triggered on demand. An
99 example would be an implementation which will read and compile a file
100 specified in a call to FindFileByName() and not require the call to Add()
101 at all. Results from this database will be cached internally here as well.
103 Args:
104 descriptor_db: A secondary source of file descriptors.
105 use_deprecated_legacy_json_field_conflicts: Unused, for compatibility with
106 C++.
107 """
109 self._internal_db = descriptor_database.DescriptorDatabase()
110 self._descriptor_db = descriptor_db
111 self._descriptors = {}
112 self._enum_descriptors = {}
113 self._service_descriptors = {}
114 self._file_descriptors = {}
115 self._toplevel_extensions = {}
116 self._top_enum_values = {}
117 # We store extensions in two two-level mappings: The first key is the
118 # descriptor of the message being extended, the second key is the extension
119 # full name or its tag number.
120 self._extensions_by_name = collections.defaultdict(dict)
121 self._extensions_by_number = collections.defaultdict(dict)
122 self._serialized_edition_defaults = (
123 python_edition_defaults._PROTOBUF_INTERNAL_PYTHON_EDITION_DEFAULTS
124 )
125 self._edition_defaults = None
126 self._feature_cache = dict()
128 def _CheckConflictRegister(self, desc, desc_name, file_name):
129 """Check if the descriptor name conflicts with another of the same name.
131 Args:
132 desc: Descriptor of a message, enum, service, extension or enum value.
133 desc_name (str): the full name of desc.
134 file_name (str): The file name of descriptor.
135 """
136 for register, descriptor_type in [
137 (self._descriptors, descriptor.Descriptor),
138 (self._enum_descriptors, descriptor.EnumDescriptor),
139 (self._service_descriptors, descriptor.ServiceDescriptor),
140 (self._toplevel_extensions, descriptor.FieldDescriptor),
141 (self._top_enum_values, descriptor.EnumValueDescriptor)]:
142 if desc_name in register:
143 old_desc = register[desc_name]
144 if isinstance(old_desc, descriptor.EnumValueDescriptor):
145 old_file = old_desc.type.file.name
146 else:
147 old_file = old_desc.file.name
149 if not isinstance(desc, descriptor_type) or (
150 old_file != file_name):
151 error_msg = ('Conflict register for file "' + file_name +
152 '": ' + desc_name +
153 ' is already defined in file "' +
154 old_file + '". Please fix the conflict by adding '
155 'package name on the proto file, or use different '
156 'name for the duplication.')
157 if isinstance(desc, descriptor.EnumValueDescriptor):
158 error_msg += ('\nNote: enum values appear as '
159 'siblings of the enum type instead of '
160 'children of it.')
162 raise TypeError(error_msg)
164 return
166 def Add(self, file_desc_proto):
167 """Adds the FileDescriptorProto and its types to this pool.
169 Args:
170 file_desc_proto (FileDescriptorProto): The file descriptor to add.
171 """
173 self._internal_db.Add(file_desc_proto)
175 def AddSerializedFile(self, serialized_file_desc_proto):
176 """Adds the FileDescriptorProto and its types to this pool.
178 Args:
179 serialized_file_desc_proto (bytes): A bytes string, serialization of the
180 :class:`FileDescriptorProto` to add.
182 Returns:
183 FileDescriptor: Descriptor for the added file.
184 """
186 # pylint: disable=g-import-not-at-top
187 from google.protobuf import descriptor_pb2
188 file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString(
189 serialized_file_desc_proto)
190 file_desc = self._ConvertFileProtoToFileDescriptor(file_desc_proto)
191 file_desc.serialized_pb = serialized_file_desc_proto
192 return file_desc
194 # Never call this method. It is for internal usage only.
195 def _AddDescriptor(self, desc):
196 """Adds a Descriptor to the pool, non-recursively.
198 If the Descriptor contains nested messages or enums, the caller must
199 explicitly register them. This method also registers the FileDescriptor
200 associated with the message.
202 Args:
203 desc: A Descriptor.
204 """
205 if not isinstance(desc, descriptor.Descriptor):
206 raise TypeError('Expected instance of descriptor.Descriptor.')
208 self._CheckConflictRegister(desc, desc.full_name, desc.file.name)
210 self._descriptors[desc.full_name] = desc
211 self._AddFileDescriptor(desc.file)
213 # Never call this method. It is for internal usage only.
214 def _AddEnumDescriptor(self, enum_desc):
215 """Adds an EnumDescriptor to the pool.
217 This method also registers the FileDescriptor associated with the enum.
219 Args:
220 enum_desc: An EnumDescriptor.
221 """
223 if not isinstance(enum_desc, descriptor.EnumDescriptor):
224 raise TypeError('Expected instance of descriptor.EnumDescriptor.')
226 file_name = enum_desc.file.name
227 self._CheckConflictRegister(enum_desc, enum_desc.full_name, file_name)
228 self._enum_descriptors[enum_desc.full_name] = enum_desc
230 # Top enum values need to be indexed.
231 # Count the number of dots to see whether the enum is toplevel or nested
232 # in a message. We cannot use enum_desc.containing_type at this stage.
233 if enum_desc.file.package:
234 top_level = (enum_desc.full_name.count('.')
235 - enum_desc.file.package.count('.') == 1)
236 else:
237 top_level = enum_desc.full_name.count('.') == 0
238 if top_level:
239 file_name = enum_desc.file.name
240 package = enum_desc.file.package
241 for enum_value in enum_desc.values:
242 full_name = _NormalizeFullyQualifiedName(
243 '.'.join((package, enum_value.name)))
244 self._CheckConflictRegister(enum_value, full_name, file_name)
245 self._top_enum_values[full_name] = enum_value
246 self._AddFileDescriptor(enum_desc.file)
248 # Never call this method. It is for internal usage only.
249 def _AddServiceDescriptor(self, service_desc):
250 """Adds a ServiceDescriptor to the pool.
252 Args:
253 service_desc: A ServiceDescriptor.
254 """
256 if not isinstance(service_desc, descriptor.ServiceDescriptor):
257 raise TypeError('Expected instance of descriptor.ServiceDescriptor.')
259 self._CheckConflictRegister(service_desc, service_desc.full_name,
260 service_desc.file.name)
261 self._service_descriptors[service_desc.full_name] = service_desc
263 # Never call this method. It is for internal usage only.
264 def _AddExtensionDescriptor(self, extension):
265 """Adds a FieldDescriptor describing an extension to the pool.
267 Args:
268 extension: A FieldDescriptor.
270 Raises:
271 AssertionError: when another extension with the same number extends the
272 same message.
273 TypeError: when the specified extension is not a
274 descriptor.FieldDescriptor.
275 """
276 if not (isinstance(extension, descriptor.FieldDescriptor) and
277 extension.is_extension):
278 raise TypeError('Expected an extension descriptor.')
280 if extension.extension_scope is None:
281 self._CheckConflictRegister(
282 extension, extension.full_name, extension.file.name)
283 self._toplevel_extensions[extension.full_name] = extension
285 try:
286 existing_desc = self._extensions_by_number[
287 extension.containing_type][extension.number]
288 except KeyError:
289 pass
290 else:
291 if extension is not existing_desc:
292 raise AssertionError(
293 'Extensions "%s" and "%s" both try to extend message type "%s" '
294 'with field number %d.' %
295 (extension.full_name, existing_desc.full_name,
296 extension.containing_type.full_name, extension.number))
298 self._extensions_by_number[extension.containing_type][
299 extension.number] = extension
300 self._extensions_by_name[extension.containing_type][
301 extension.full_name] = extension
303 # Also register MessageSet extensions with the type name.
304 if _IsMessageSetExtension(extension):
305 self._extensions_by_name[extension.containing_type][
306 extension.message_type.full_name] = extension
308 if hasattr(extension.containing_type, '_concrete_class'):
309 python_message._AttachFieldHelpers(
310 extension.containing_type._concrete_class, extension)
312 # Never call this method. It is for internal usage only.
313 def _InternalAddFileDescriptor(self, file_desc):
314 """Adds a FileDescriptor to the pool, non-recursively.
316 If the FileDescriptor contains messages or enums, the caller must explicitly
317 register them.
319 Args:
320 file_desc: A FileDescriptor.
321 """
323 self._AddFileDescriptor(file_desc)
325 def _AddFileDescriptor(self, file_desc):
326 """Adds a FileDescriptor to the pool, non-recursively.
328 If the FileDescriptor contains messages or enums, the caller must explicitly
329 register them.
331 Args:
332 file_desc: A FileDescriptor.
333 """
335 if not isinstance(file_desc, descriptor.FileDescriptor):
336 raise TypeError('Expected instance of descriptor.FileDescriptor.')
337 self._file_descriptors[file_desc.name] = file_desc
339 def FindFileByName(self, file_name):
340 """Gets a FileDescriptor by file name.
342 Args:
343 file_name (str): The path to the file to get a descriptor for.
345 Returns:
346 FileDescriptor: The descriptor for the named file.
348 Raises:
349 KeyError: if the file cannot be found in the pool.
350 """
352 try:
353 return self._file_descriptors[file_name]
354 except KeyError:
355 pass
357 try:
358 file_proto = self._internal_db.FindFileByName(file_name)
359 except KeyError as error:
360 if self._descriptor_db:
361 file_proto = self._descriptor_db.FindFileByName(file_name)
362 else:
363 raise error
364 if not file_proto:
365 raise KeyError('Cannot find a file named %s' % file_name)
366 return self._ConvertFileProtoToFileDescriptor(file_proto)
368 def FindFileContainingSymbol(self, symbol):
369 """Gets the FileDescriptor for the file containing the specified symbol.
371 Args:
372 symbol (str): The name of the symbol to search for.
374 Returns:
375 FileDescriptor: Descriptor for the file that contains the specified
376 symbol.
378 Raises:
379 KeyError: if the file cannot be found in the pool.
380 """
382 symbol = _NormalizeFullyQualifiedName(symbol)
383 try:
384 return self._InternalFindFileContainingSymbol(symbol)
385 except KeyError:
386 pass
388 try:
389 # Try fallback database. Build and find again if possible.
390 self._FindFileContainingSymbolInDb(symbol)
391 return self._InternalFindFileContainingSymbol(symbol)
392 except KeyError:
393 raise KeyError('Cannot find a file containing %s' % symbol)
395 def _InternalFindFileContainingSymbol(self, symbol):
396 """Gets the already built FileDescriptor containing the specified symbol.
398 Args:
399 symbol (str): The name of the symbol to search for.
401 Returns:
402 FileDescriptor: Descriptor for the file that contains the specified
403 symbol.
405 Raises:
406 KeyError: if the file cannot be found in the pool.
407 """
408 try:
409 return self._descriptors[symbol].file
410 except KeyError:
411 pass
413 try:
414 return self._enum_descriptors[symbol].file
415 except KeyError:
416 pass
418 try:
419 return self._service_descriptors[symbol].file
420 except KeyError:
421 pass
423 try:
424 return self._top_enum_values[symbol].type.file
425 except KeyError:
426 pass
428 try:
429 return self._toplevel_extensions[symbol].file
430 except KeyError:
431 pass
433 # Try fields, enum values and nested extensions inside a message.
434 top_name, _, sub_name = symbol.rpartition('.')
435 try:
436 message = self.FindMessageTypeByName(top_name)
437 assert (sub_name in message.extensions_by_name or
438 sub_name in message.fields_by_name or
439 sub_name in message.enum_values_by_name)
440 return message.file
441 except (KeyError, AssertionError):
442 raise KeyError('Cannot find a file containing %s' % symbol)
444 def FindMessageTypeByName(self, full_name):
445 """Loads the named descriptor from the pool.
447 Args:
448 full_name (str): The full name of the descriptor to load.
450 Returns:
451 Descriptor: The descriptor for the named type.
453 Raises:
454 KeyError: if the message cannot be found in the pool.
455 """
457 full_name = _NormalizeFullyQualifiedName(full_name)
458 if full_name not in self._descriptors:
459 self._FindFileContainingSymbolInDb(full_name)
460 return self._descriptors[full_name]
462 def FindEnumTypeByName(self, full_name):
463 """Loads the named enum descriptor from the pool.
465 Args:
466 full_name (str): The full name of the enum descriptor to load.
468 Returns:
469 EnumDescriptor: The enum descriptor for the named type.
471 Raises:
472 KeyError: if the enum cannot be found in the pool.
473 """
475 full_name = _NormalizeFullyQualifiedName(full_name)
476 if full_name not in self._enum_descriptors:
477 self._FindFileContainingSymbolInDb(full_name)
478 return self._enum_descriptors[full_name]
480 def FindFieldByName(self, full_name):
481 """Loads the named field descriptor from the pool.
483 Args:
484 full_name (str): The full name of the field descriptor to load.
486 Returns:
487 FieldDescriptor: The field descriptor for the named field.
489 Raises:
490 KeyError: if the field cannot be found in the pool.
491 """
492 full_name = _NormalizeFullyQualifiedName(full_name)
493 message_name, _, field_name = full_name.rpartition('.')
494 message_descriptor = self.FindMessageTypeByName(message_name)
495 return message_descriptor.fields_by_name[field_name]
497 def FindOneofByName(self, full_name):
498 """Loads the named oneof descriptor from the pool.
500 Args:
501 full_name (str): The full name of the oneof descriptor to load.
503 Returns:
504 OneofDescriptor: The oneof descriptor for the named oneof.
506 Raises:
507 KeyError: if the oneof cannot be found in the pool.
508 """
509 full_name = _NormalizeFullyQualifiedName(full_name)
510 message_name, _, oneof_name = full_name.rpartition('.')
511 message_descriptor = self.FindMessageTypeByName(message_name)
512 return message_descriptor.oneofs_by_name[oneof_name]
514 def FindExtensionByName(self, full_name):
515 """Loads the named extension descriptor from the pool.
517 Args:
518 full_name (str): The full name of the extension descriptor to load.
520 Returns:
521 FieldDescriptor: The field descriptor for the named extension.
523 Raises:
524 KeyError: if the extension cannot be found in the pool.
525 """
526 full_name = _NormalizeFullyQualifiedName(full_name)
527 try:
528 # The proto compiler does not give any link between the FileDescriptor
529 # and top-level extensions unless the FileDescriptorProto is added to
530 # the DescriptorDatabase, but this can impact memory usage.
531 # So we registered these extensions by name explicitly.
532 return self._toplevel_extensions[full_name]
533 except KeyError:
534 pass
535 message_name, _, extension_name = full_name.rpartition('.')
536 try:
537 # Most extensions are nested inside a message.
538 scope = self.FindMessageTypeByName(message_name)
539 except KeyError:
540 # Some extensions are defined at file scope.
541 scope = self._FindFileContainingSymbolInDb(full_name)
542 return scope.extensions_by_name[extension_name]
544 def FindExtensionByNumber(self, message_descriptor, number):
545 """Gets the extension of the specified message with the specified number.
547 Extensions have to be registered to this pool by calling :func:`Add` or
548 :func:`AddExtensionDescriptor`.
550 Args:
551 message_descriptor (Descriptor): descriptor of the extended message.
552 number (int): Number of the extension field.
554 Returns:
555 FieldDescriptor: The descriptor for the extension.
557 Raises:
558 KeyError: when no extension with the given number is known for the
559 specified message.
560 """
561 try:
562 return self._extensions_by_number[message_descriptor][number]
563 except KeyError:
564 self._TryLoadExtensionFromDB(message_descriptor, number)
565 return self._extensions_by_number[message_descriptor][number]
567 def FindAllExtensions(self, message_descriptor):
568 """Gets all the known extensions of a given message.
570 Extensions have to be registered to this pool by build related
571 :func:`Add` or :func:`AddExtensionDescriptor`.
573 Args:
574 message_descriptor (Descriptor): Descriptor of the extended message.
576 Returns:
577 list[FieldDescriptor]: Field descriptors describing the extensions.
578 """
579 # Fallback to descriptor db if FindAllExtensionNumbers is provided.
580 if self._descriptor_db and hasattr(
581 self._descriptor_db, 'FindAllExtensionNumbers'):
582 full_name = message_descriptor.full_name
583 all_numbers = self._descriptor_db.FindAllExtensionNumbers(full_name)
584 for number in all_numbers:
585 if number in self._extensions_by_number[message_descriptor]:
586 continue
587 self._TryLoadExtensionFromDB(message_descriptor, number)
589 return list(self._extensions_by_number[message_descriptor].values())
591 def _TryLoadExtensionFromDB(self, message_descriptor, number):
592 """Try to Load extensions from descriptor db.
594 Args:
595 message_descriptor: descriptor of the extended message.
596 number: the extension number that needs to be loaded.
597 """
598 if not self._descriptor_db:
599 return
600 # Only supported when FindFileContainingExtension is provided.
601 if not hasattr(
602 self._descriptor_db, 'FindFileContainingExtension'):
603 return
605 full_name = message_descriptor.full_name
606 file_proto = self._descriptor_db.FindFileContainingExtension(
607 full_name, number)
609 if file_proto is None:
610 return
612 try:
613 self._ConvertFileProtoToFileDescriptor(file_proto)
614 except:
615 warn_msg = ('Unable to load proto file %s for extension number %d.' %
616 (file_proto.name, number))
617 warnings.warn(warn_msg, RuntimeWarning)
619 def FindServiceByName(self, full_name):
620 """Loads the named service descriptor from the pool.
622 Args:
623 full_name (str): The full name of the service descriptor to load.
625 Returns:
626 ServiceDescriptor: The service descriptor for the named service.
628 Raises:
629 KeyError: if the service cannot be found in the pool.
630 """
631 full_name = _NormalizeFullyQualifiedName(full_name)
632 if full_name not in self._service_descriptors:
633 self._FindFileContainingSymbolInDb(full_name)
634 return self._service_descriptors[full_name]
636 def FindMethodByName(self, full_name):
637 """Loads the named service method descriptor from the pool.
639 Args:
640 full_name (str): The full name of the method descriptor to load.
642 Returns:
643 MethodDescriptor: The method descriptor for the service method.
645 Raises:
646 KeyError: if the method cannot be found in the pool.
647 """
648 full_name = _NormalizeFullyQualifiedName(full_name)
649 service_name, _, method_name = full_name.rpartition('.')
650 service_descriptor = self.FindServiceByName(service_name)
651 return service_descriptor.methods_by_name[method_name]
653 def SetFeatureSetDefaults(self, defaults):
654 """Sets the default feature mappings used during the build.
656 Args:
657 defaults: a FeatureSetDefaults message containing the new mappings.
658 """
659 if self._edition_defaults is not None:
660 raise ValueError(
661 "Feature set defaults can't be changed once the pool has started"
662 ' building!'
663 )
665 # pylint: disable=g-import-not-at-top
666 from google.protobuf import descriptor_pb2
668 if not isinstance(defaults, descriptor_pb2.FeatureSetDefaults):
669 raise TypeError('SetFeatureSetDefaults called with invalid type')
672 if defaults.minimum_edition > defaults.maximum_edition:
673 raise ValueError(
674 'Invalid edition range %s to %s'
675 % (
676 descriptor_pb2.Edition.Name(defaults.minimum_edition),
677 descriptor_pb2.Edition.Name(defaults.maximum_edition),
678 )
679 )
681 prev_edition = descriptor_pb2.Edition.EDITION_UNKNOWN
682 for d in defaults.defaults:
683 if d.edition == descriptor_pb2.Edition.EDITION_UNKNOWN:
684 raise ValueError('Invalid edition EDITION_UNKNOWN specified')
685 if prev_edition >= d.edition:
686 raise ValueError(
687 'Feature set defaults are not strictly increasing. %s is greater'
688 ' than or equal to %s'
689 % (
690 descriptor_pb2.Edition.Name(prev_edition),
691 descriptor_pb2.Edition.Name(d.edition),
692 )
693 )
694 prev_edition = d.edition
695 self._edition_defaults = defaults
697 def _CreateDefaultFeatures(self, edition):
698 """Creates a FeatureSet message with defaults for a specific edition.
700 Args:
701 edition: the edition to generate defaults for.
703 Returns:
704 A FeatureSet message with defaults for a specific edition.
705 """
706 # pylint: disable=g-import-not-at-top
707 from google.protobuf import descriptor_pb2
709 with _edition_defaults_lock:
710 if not self._edition_defaults:
711 self._edition_defaults = descriptor_pb2.FeatureSetDefaults()
712 self._edition_defaults.ParseFromString(
713 self._serialized_edition_defaults
714 )
716 if edition < self._edition_defaults.minimum_edition:
717 raise TypeError(
718 'Edition %s is earlier than the minimum supported edition %s!'
719 % (
720 descriptor_pb2.Edition.Name(edition),
721 descriptor_pb2.Edition.Name(
722 self._edition_defaults.minimum_edition
723 ),
724 )
725 )
726 if edition > self._edition_defaults.maximum_edition:
727 raise TypeError(
728 'Edition %s is later than the maximum supported edition %s!'
729 % (
730 descriptor_pb2.Edition.Name(edition),
731 descriptor_pb2.Edition.Name(
732 self._edition_defaults.maximum_edition
733 ),
734 )
735 )
736 found = None
737 for d in self._edition_defaults.defaults:
738 if d.edition > edition:
739 break
740 found = d.features
741 if found is None:
742 raise TypeError(
743 'No valid default found for edition %s!'
744 % descriptor_pb2.Edition.Name(edition)
745 )
747 defaults = descriptor_pb2.FeatureSet()
748 defaults.CopyFrom(found)
749 return defaults
751 def _InternFeatures(self, features):
752 serialized = features.SerializeToString()
753 with _edition_defaults_lock:
754 cached = self._feature_cache.get(serialized)
755 if cached is None:
756 self._feature_cache[serialized] = features
757 cached = features
758 return cached
760 def _FindFileContainingSymbolInDb(self, symbol):
761 """Finds the file in descriptor DB containing the specified symbol.
763 Args:
764 symbol (str): The name of the symbol to search for.
766 Returns:
767 FileDescriptor: The file that contains the specified symbol.
769 Raises:
770 KeyError: if the file cannot be found in the descriptor database.
771 """
772 try:
773 file_proto = self._internal_db.FindFileContainingSymbol(symbol)
774 except KeyError as error:
775 if self._descriptor_db:
776 file_proto = self._descriptor_db.FindFileContainingSymbol(symbol)
777 else:
778 raise error
779 if not file_proto:
780 raise KeyError('Cannot find a file containing %s' % symbol)
781 return self._ConvertFileProtoToFileDescriptor(file_proto)
783 def _ConvertFileProtoToFileDescriptor(self, file_proto):
784 """Creates a FileDescriptor from a proto or returns a cached copy.
786 This method also has the side effect of loading all the symbols found in
787 the file into the appropriate dictionaries in the pool.
789 Args:
790 file_proto: The proto to convert.
792 Returns:
793 A FileDescriptor matching the passed in proto.
794 """
795 if file_proto.name not in self._file_descriptors:
796 built_deps = list(self._GetDeps(file_proto.dependency))
797 direct_deps = [self.FindFileByName(n) for n in file_proto.dependency]
798 public_deps = [direct_deps[i] for i in file_proto.public_dependency]
800 # pylint: disable=g-import-not-at-top
801 from google.protobuf import descriptor_pb2
803 file_descriptor = descriptor.FileDescriptor(
804 pool=self,
805 name=file_proto.name,
806 package=file_proto.package,
807 syntax=file_proto.syntax,
808 edition=descriptor_pb2.Edition.Name(file_proto.edition),
809 options=_OptionsOrNone(file_proto),
810 serialized_pb=file_proto.SerializeToString(),
811 dependencies=direct_deps,
812 public_dependencies=public_deps,
813 # pylint: disable=protected-access
814 create_key=descriptor._internal_create_key,
815 )
816 scope = {}
818 # This loop extracts all the message and enum types from all the
819 # dependencies of the file_proto. This is necessary to create the
820 # scope of available message types when defining the passed in
821 # file proto.
822 for dependency in built_deps:
823 scope.update(self._ExtractSymbols(
824 dependency.message_types_by_name.values()))
825 scope.update((_PrefixWithDot(enum.full_name), enum)
826 for enum in dependency.enum_types_by_name.values())
828 for message_type in file_proto.message_type:
829 message_desc = self._ConvertMessageDescriptor(
830 message_type, file_proto.package, file_descriptor, scope,
831 file_proto.syntax)
832 file_descriptor.message_types_by_name[message_desc.name] = (
833 message_desc)
835 for enum_type in file_proto.enum_type:
836 file_descriptor.enum_types_by_name[enum_type.name] = (
837 self._ConvertEnumDescriptor(enum_type, file_proto.package,
838 file_descriptor, None, scope, True))
840 for index, extension_proto in enumerate(file_proto.extension):
841 extension_desc = self._MakeFieldDescriptor(
842 extension_proto, file_proto.package, index, file_descriptor,
843 is_extension=True)
844 extension_desc.containing_type = self._GetTypeFromScope(
845 file_descriptor.package, extension_proto.extendee, scope)
846 self._SetFieldType(extension_proto, extension_desc,
847 file_descriptor.package, scope)
848 file_descriptor.extensions_by_name[extension_desc.name] = (
849 extension_desc)
851 for desc_proto in file_proto.message_type:
852 self._SetAllFieldTypes(file_proto.package, desc_proto, scope)
854 if file_proto.package:
855 desc_proto_prefix = _PrefixWithDot(file_proto.package)
856 else:
857 desc_proto_prefix = ''
859 for desc_proto in file_proto.message_type:
860 desc = self._GetTypeFromScope(
861 desc_proto_prefix, desc_proto.name, scope)
862 file_descriptor.message_types_by_name[desc_proto.name] = desc
864 for index, service_proto in enumerate(file_proto.service):
865 file_descriptor.services_by_name[service_proto.name] = (
866 self._MakeServiceDescriptor(service_proto, index, scope,
867 file_proto.package, file_descriptor))
869 self._file_descriptors[file_proto.name] = file_descriptor
871 # Add extensions to the pool
872 def AddExtensionForNested(message_type):
873 for nested in message_type.nested_types:
874 AddExtensionForNested(nested)
875 for extension in message_type.extensions:
876 self._AddExtensionDescriptor(extension)
878 file_desc = self._file_descriptors[file_proto.name]
879 for extension in file_desc.extensions_by_name.values():
880 self._AddExtensionDescriptor(extension)
881 for message_type in file_desc.message_types_by_name.values():
882 AddExtensionForNested(message_type)
884 return file_desc
886 def _ConvertMessageDescriptor(self, desc_proto, package=None, file_desc=None,
887 scope=None, syntax=None):
888 """Adds the proto to the pool in the specified package.
890 Args:
891 desc_proto: The descriptor_pb2.DescriptorProto protobuf message.
892 package: The package the proto should be located in.
893 file_desc: The file containing this message.
894 scope: Dict mapping short and full symbols to message and enum types.
895 syntax: string indicating syntax of the file ("proto2" or "proto3")
897 Returns:
898 The added descriptor.
899 """
901 if package:
902 desc_name = '.'.join((package, desc_proto.name))
903 else:
904 desc_name = desc_proto.name
906 if file_desc is None:
907 file_name = None
908 else:
909 file_name = file_desc.name
911 if scope is None:
912 scope = {}
914 nested = [
915 self._ConvertMessageDescriptor(
916 nested, desc_name, file_desc, scope, syntax)
917 for nested in desc_proto.nested_type]
918 enums = [
919 self._ConvertEnumDescriptor(enum, desc_name, file_desc, None,
920 scope, False)
921 for enum in desc_proto.enum_type]
922 fields = [self._MakeFieldDescriptor(field, desc_name, index, file_desc)
923 for index, field in enumerate(desc_proto.field)]
924 extensions = [
925 self._MakeFieldDescriptor(extension, desc_name, index, file_desc,
926 is_extension=True)
927 for index, extension in enumerate(desc_proto.extension)]
928 oneofs = [
929 # pylint: disable=g-complex-comprehension
930 descriptor.OneofDescriptor(
931 desc.name,
932 '.'.join((desc_name, desc.name)),
933 index,
934 None,
935 [],
936 _OptionsOrNone(desc),
937 # pylint: disable=protected-access
938 create_key=descriptor._internal_create_key)
939 for index, desc in enumerate(desc_proto.oneof_decl)
940 ]
941 extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range]
942 if extension_ranges:
943 is_extendable = True
944 else:
945 is_extendable = False
946 desc = descriptor.Descriptor(
947 name=desc_proto.name,
948 full_name=desc_name,
949 filename=file_name,
950 containing_type=None,
951 fields=fields,
952 oneofs=oneofs,
953 nested_types=nested,
954 enum_types=enums,
955 extensions=extensions,
956 options=_OptionsOrNone(desc_proto),
957 is_extendable=is_extendable,
958 extension_ranges=extension_ranges,
959 file=file_desc,
960 serialized_start=None,
961 serialized_end=None,
962 is_map_entry=desc_proto.options.map_entry,
963 # pylint: disable=protected-access
964 create_key=descriptor._internal_create_key,
965 )
966 for nested in desc.nested_types:
967 nested.containing_type = desc
968 for enum in desc.enum_types:
969 enum.containing_type = desc
970 for field_index, field_desc in enumerate(desc_proto.field):
971 if field_desc.HasField('oneof_index'):
972 oneof_index = field_desc.oneof_index
973 oneofs[oneof_index].fields.append(fields[field_index])
974 fields[field_index].containing_oneof = oneofs[oneof_index]
976 scope[_PrefixWithDot(desc_name)] = desc
977 self._CheckConflictRegister(desc, desc.full_name, desc.file.name)
978 self._descriptors[desc_name] = desc
979 return desc
981 def _ConvertEnumDescriptor(self, enum_proto, package=None, file_desc=None,
982 containing_type=None, scope=None, top_level=False):
983 """Make a protobuf EnumDescriptor given an EnumDescriptorProto protobuf.
985 Args:
986 enum_proto: The descriptor_pb2.EnumDescriptorProto protobuf message.
987 package: Optional package name for the new message EnumDescriptor.
988 file_desc: The file containing the enum descriptor.
989 containing_type: The type containing this enum.
990 scope: Scope containing available types.
991 top_level: If True, the enum is a top level symbol. If False, the enum
992 is defined inside a message.
994 Returns:
995 The added descriptor
996 """
998 if package:
999 enum_name = '.'.join((package, enum_proto.name))
1000 else:
1001 enum_name = enum_proto.name
1003 if file_desc is None:
1004 file_name = None
1005 else:
1006 file_name = file_desc.name
1008 values = [self._MakeEnumValueDescriptor(value, index)
1009 for index, value in enumerate(enum_proto.value)]
1010 desc = descriptor.EnumDescriptor(name=enum_proto.name,
1011 full_name=enum_name,
1012 filename=file_name,
1013 file=file_desc,
1014 values=values,
1015 containing_type=containing_type,
1016 options=_OptionsOrNone(enum_proto),
1017 # pylint: disable=protected-access
1018 create_key=descriptor._internal_create_key)
1019 scope['.%s' % enum_name] = desc
1020 self._CheckConflictRegister(desc, desc.full_name, desc.file.name)
1021 self._enum_descriptors[enum_name] = desc
1023 # Add top level enum values.
1024 if top_level:
1025 for value in values:
1026 full_name = _NormalizeFullyQualifiedName(
1027 '.'.join((package, value.name)))
1028 self._CheckConflictRegister(value, full_name, file_name)
1029 self._top_enum_values[full_name] = value
1031 return desc
1033 def _MakeFieldDescriptor(self, field_proto, message_name, index,
1034 file_desc, is_extension=False):
1035 """Creates a field descriptor from a FieldDescriptorProto.
1037 For message and enum type fields, this method will do a look up
1038 in the pool for the appropriate descriptor for that type. If it
1039 is unavailable, it will fall back to the _source function to
1040 create it. If this type is still unavailable, construction will
1041 fail.
1043 Args:
1044 field_proto: The proto describing the field.
1045 message_name: The name of the containing message.
1046 index: Index of the field
1047 file_desc: The file containing the field descriptor.
1048 is_extension: Indication that this field is for an extension.
1050 Returns:
1051 An initialized FieldDescriptor object
1052 """
1054 if message_name:
1055 full_name = '.'.join((message_name, field_proto.name))
1056 else:
1057 full_name = field_proto.name
1059 if field_proto.json_name:
1060 json_name = field_proto.json_name
1061 else:
1062 json_name = None
1064 return descriptor.FieldDescriptor(
1065 name=field_proto.name,
1066 full_name=full_name,
1067 index=index,
1068 number=field_proto.number,
1069 type=field_proto.type,
1070 cpp_type=None,
1071 message_type=None,
1072 enum_type=None,
1073 containing_type=None,
1074 label=field_proto.label,
1075 has_default_value=False,
1076 default_value=None,
1077 is_extension=is_extension,
1078 extension_scope=None,
1079 options=_OptionsOrNone(field_proto),
1080 json_name=json_name,
1081 file=file_desc,
1082 # pylint: disable=protected-access
1083 create_key=descriptor._internal_create_key)
1085 def _SetAllFieldTypes(self, package, desc_proto, scope):
1086 """Sets all the descriptor's fields's types.
1088 This method also sets the containing types on any extensions.
1090 Args:
1091 package: The current package of desc_proto.
1092 desc_proto: The message descriptor to update.
1093 scope: Enclosing scope of available types.
1094 """
1096 package = _PrefixWithDot(package)
1098 main_desc = self._GetTypeFromScope(package, desc_proto.name, scope)
1100 if package == '.':
1101 nested_package = _PrefixWithDot(desc_proto.name)
1102 else:
1103 nested_package = '.'.join([package, desc_proto.name])
1105 for field_proto, field_desc in zip(desc_proto.field, main_desc.fields):
1106 self._SetFieldType(field_proto, field_desc, nested_package, scope)
1108 for extension_proto, extension_desc in (
1109 zip(desc_proto.extension, main_desc.extensions)):
1110 extension_desc.containing_type = self._GetTypeFromScope(
1111 nested_package, extension_proto.extendee, scope)
1112 self._SetFieldType(extension_proto, extension_desc, nested_package, scope)
1114 for nested_type in desc_proto.nested_type:
1115 self._SetAllFieldTypes(nested_package, nested_type, scope)
1117 def _SetFieldType(self, field_proto, field_desc, package, scope):
1118 """Sets the field's type, cpp_type, message_type and enum_type.
1120 Args:
1121 field_proto: Data about the field in proto format.
1122 field_desc: The descriptor to modify.
1123 package: The package the field's container is in.
1124 scope: Enclosing scope of available types.
1125 """
1126 if field_proto.type_name:
1127 desc = self._GetTypeFromScope(package, field_proto.type_name, scope)
1128 else:
1129 desc = None
1131 if not field_proto.HasField('type'):
1132 if isinstance(desc, descriptor.Descriptor):
1133 field_proto.type = descriptor.FieldDescriptor.TYPE_MESSAGE
1134 else:
1135 field_proto.type = descriptor.FieldDescriptor.TYPE_ENUM
1137 field_desc.cpp_type = descriptor.FieldDescriptor.ProtoTypeToCppProtoType(
1138 field_proto.type)
1140 if (field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE
1141 or field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP):
1142 field_desc.message_type = desc
1144 if field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM:
1145 field_desc.enum_type = desc
1147 if field_proto.label == descriptor.FieldDescriptor.LABEL_REPEATED:
1148 field_desc.has_default_value = False
1149 field_desc.default_value = []
1150 elif field_proto.HasField('default_value'):
1151 field_desc.has_default_value = True
1152 if (field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE or
1153 field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT):
1154 field_desc.default_value = float(field_proto.default_value)
1155 elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING:
1156 field_desc.default_value = field_proto.default_value
1157 elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL:
1158 field_desc.default_value = field_proto.default_value.lower() == 'true'
1159 elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM:
1160 field_desc.default_value = field_desc.enum_type.values_by_name[
1161 field_proto.default_value].number
1162 elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES:
1163 field_desc.default_value = text_encoding.CUnescape(
1164 field_proto.default_value)
1165 elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE:
1166 field_desc.default_value = None
1167 else:
1168 # All other types are of the "int" type.
1169 field_desc.default_value = int(field_proto.default_value)
1170 else:
1171 field_desc.has_default_value = False
1172 if (field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE or
1173 field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT):
1174 field_desc.default_value = 0.0
1175 elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING:
1176 field_desc.default_value = u''
1177 elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL:
1178 field_desc.default_value = False
1179 elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM:
1180 field_desc.default_value = field_desc.enum_type.values[0].number
1181 elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES:
1182 field_desc.default_value = b''
1183 elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE:
1184 field_desc.default_value = None
1185 elif field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP:
1186 field_desc.default_value = None
1187 else:
1188 # All other types are of the "int" type.
1189 field_desc.default_value = 0
1191 field_desc.type = field_proto.type
1193 def _MakeEnumValueDescriptor(self, value_proto, index):
1194 """Creates a enum value descriptor object from a enum value proto.
1196 Args:
1197 value_proto: The proto describing the enum value.
1198 index: The index of the enum value.
1200 Returns:
1201 An initialized EnumValueDescriptor object.
1202 """
1204 return descriptor.EnumValueDescriptor(
1205 name=value_proto.name,
1206 index=index,
1207 number=value_proto.number,
1208 options=_OptionsOrNone(value_proto),
1209 type=None,
1210 # pylint: disable=protected-access
1211 create_key=descriptor._internal_create_key)
1213 def _MakeServiceDescriptor(self, service_proto, service_index, scope,
1214 package, file_desc):
1215 """Make a protobuf ServiceDescriptor given a ServiceDescriptorProto.
1217 Args:
1218 service_proto: The descriptor_pb2.ServiceDescriptorProto protobuf message.
1219 service_index: The index of the service in the File.
1220 scope: Dict mapping short and full symbols to message and enum types.
1221 package: Optional package name for the new message EnumDescriptor.
1222 file_desc: The file containing the service descriptor.
1224 Returns:
1225 The added descriptor.
1226 """
1228 if package:
1229 service_name = '.'.join((package, service_proto.name))
1230 else:
1231 service_name = service_proto.name
1233 methods = [self._MakeMethodDescriptor(method_proto, service_name, package,
1234 scope, index)
1235 for index, method_proto in enumerate(service_proto.method)]
1236 desc = descriptor.ServiceDescriptor(
1237 name=service_proto.name,
1238 full_name=service_name,
1239 index=service_index,
1240 methods=methods,
1241 options=_OptionsOrNone(service_proto),
1242 file=file_desc,
1243 # pylint: disable=protected-access
1244 create_key=descriptor._internal_create_key)
1245 self._CheckConflictRegister(desc, desc.full_name, desc.file.name)
1246 self._service_descriptors[service_name] = desc
1247 return desc
1249 def _MakeMethodDescriptor(self, method_proto, service_name, package, scope,
1250 index):
1251 """Creates a method descriptor from a MethodDescriptorProto.
1253 Args:
1254 method_proto: The proto describing the method.
1255 service_name: The name of the containing service.
1256 package: Optional package name to look up for types.
1257 scope: Scope containing available types.
1258 index: Index of the method in the service.
1260 Returns:
1261 An initialized MethodDescriptor object.
1262 """
1263 full_name = '.'.join((service_name, method_proto.name))
1264 input_type = self._GetTypeFromScope(
1265 package, method_proto.input_type, scope)
1266 output_type = self._GetTypeFromScope(
1267 package, method_proto.output_type, scope)
1268 return descriptor.MethodDescriptor(
1269 name=method_proto.name,
1270 full_name=full_name,
1271 index=index,
1272 containing_service=None,
1273 input_type=input_type,
1274 output_type=output_type,
1275 client_streaming=method_proto.client_streaming,
1276 server_streaming=method_proto.server_streaming,
1277 options=_OptionsOrNone(method_proto),
1278 # pylint: disable=protected-access
1279 create_key=descriptor._internal_create_key)
1281 def _ExtractSymbols(self, descriptors):
1282 """Pulls out all the symbols from descriptor protos.
1284 Args:
1285 descriptors: The messages to extract descriptors from.
1286 Yields:
1287 A two element tuple of the type name and descriptor object.
1288 """
1290 for desc in descriptors:
1291 yield (_PrefixWithDot(desc.full_name), desc)
1292 for symbol in self._ExtractSymbols(desc.nested_types):
1293 yield symbol
1294 for enum in desc.enum_types:
1295 yield (_PrefixWithDot(enum.full_name), enum)
1297 def _GetDeps(self, dependencies, visited=None):
1298 """Recursively finds dependencies for file protos.
1300 Args:
1301 dependencies: The names of the files being depended on.
1302 visited: The names of files already found.
1304 Yields:
1305 Each direct and indirect dependency.
1306 """
1308 visited = visited or set()
1309 for dependency in dependencies:
1310 if dependency not in visited:
1311 visited.add(dependency)
1312 dep_desc = self.FindFileByName(dependency)
1313 yield dep_desc
1314 public_files = [d.name for d in dep_desc.public_dependencies]
1315 yield from self._GetDeps(public_files, visited)
1317 def _GetTypeFromScope(self, package, type_name, scope):
1318 """Finds a given type name in the current scope.
1320 Args:
1321 package: The package the proto should be located in.
1322 type_name: The name of the type to be found in the scope.
1323 scope: Dict mapping short and full symbols to message and enum types.
1325 Returns:
1326 The descriptor for the requested type.
1327 """
1328 if type_name not in scope:
1329 components = _PrefixWithDot(package).split('.')
1330 while components:
1331 possible_match = '.'.join(components + [type_name])
1332 if possible_match in scope:
1333 type_name = possible_match
1334 break
1335 else:
1336 components.pop(-1)
1337 return scope[type_name]
1340def _PrefixWithDot(name):
1341 return name if name.startswith('.') else '.%s' % name
1344if _USE_C_DESCRIPTORS:
1345 # TODO: This pool could be constructed from Python code, when we
1346 # support a flag like 'use_cpp_generated_pool=True'.
1347 # pylint: disable=protected-access
1348 _DEFAULT = descriptor._message.default_pool
1349else:
1350 _DEFAULT = DescriptorPool()
1353def Default():
1354 return _DEFAULT