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