Brian Silverman | 9c614bc | 2016-02-15 20:20:02 -0500 | [diff] [blame] | 1 | # Protocol Buffers - Google's data interchange format |
| 2 | # Copyright 2008 Google Inc. All rights reserved. |
| 3 | # https://developers.google.com/protocol-buffers/ |
| 4 | # |
| 5 | # Redistribution and use in source and binary forms, with or without |
| 6 | # modification, are permitted provided that the following conditions are |
| 7 | # met: |
| 8 | # |
| 9 | # * Redistributions of source code must retain the above copyright |
| 10 | # notice, this list of conditions and the following disclaimer. |
| 11 | # * Redistributions in binary form must reproduce the above |
| 12 | # copyright notice, this list of conditions and the following disclaimer |
| 13 | # in the documentation and/or other materials provided with the |
| 14 | # distribution. |
| 15 | # * Neither the name of Google Inc. nor the names of its |
| 16 | # contributors may be used to endorse or promote products derived from |
| 17 | # this software without specific prior written permission. |
| 18 | # |
| 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | |
| 31 | """Dynamic Protobuf class creator.""" |
| 32 | |
| 33 | try: |
| 34 | from collections import OrderedDict |
| 35 | except ImportError: |
| 36 | from ordereddict import OrderedDict #PY26 |
| 37 | import hashlib |
| 38 | import os |
| 39 | |
| 40 | from google.protobuf import descriptor_pb2 |
| 41 | from google.protobuf import message_factory |
| 42 | |
| 43 | |
| 44 | def _GetMessageFromFactory(factory, full_name): |
| 45 | """Get a proto class from the MessageFactory by name. |
| 46 | |
| 47 | Args: |
| 48 | factory: a MessageFactory instance. |
| 49 | full_name: str, the fully qualified name of the proto type. |
| 50 | Returns: |
| 51 | A class, for the type identified by full_name. |
| 52 | Raises: |
| 53 | KeyError, if the proto is not found in the factory's descriptor pool. |
| 54 | """ |
| 55 | proto_descriptor = factory.pool.FindMessageTypeByName(full_name) |
| 56 | proto_cls = factory.GetPrototype(proto_descriptor) |
| 57 | return proto_cls |
| 58 | |
| 59 | |
| 60 | def MakeSimpleProtoClass(fields, full_name=None, pool=None): |
| 61 | """Create a Protobuf class whose fields are basic types. |
| 62 | |
| 63 | Note: this doesn't validate field names! |
| 64 | |
| 65 | Args: |
| 66 | fields: dict of {name: field_type} mappings for each field in the proto. If |
| 67 | this is an OrderedDict the order will be maintained, otherwise the |
| 68 | fields will be sorted by name. |
| 69 | full_name: optional str, the fully-qualified name of the proto type. |
| 70 | pool: optional DescriptorPool instance. |
| 71 | Returns: |
| 72 | a class, the new protobuf class with a FileDescriptor. |
| 73 | """ |
| 74 | factory = message_factory.MessageFactory(pool=pool) |
| 75 | |
| 76 | if full_name is not None: |
| 77 | try: |
| 78 | proto_cls = _GetMessageFromFactory(factory, full_name) |
| 79 | return proto_cls |
| 80 | except KeyError: |
| 81 | # The factory's DescriptorPool doesn't know about this class yet. |
| 82 | pass |
| 83 | |
| 84 | # Get a list of (name, field_type) tuples from the fields dict. If fields was |
| 85 | # an OrderedDict we keep the order, but otherwise we sort the field to ensure |
| 86 | # consistent ordering. |
| 87 | field_items = fields.items() |
| 88 | if not isinstance(fields, OrderedDict): |
| 89 | field_items = sorted(field_items) |
| 90 | |
| 91 | # Use a consistent file name that is unlikely to conflict with any imported |
| 92 | # proto files. |
| 93 | fields_hash = hashlib.sha1() |
| 94 | for f_name, f_type in field_items: |
| 95 | fields_hash.update(f_name.encode('utf-8')) |
| 96 | fields_hash.update(str(f_type).encode('utf-8')) |
| 97 | proto_file_name = fields_hash.hexdigest() + '.proto' |
| 98 | |
| 99 | # If the proto is anonymous, use the same hash to name it. |
| 100 | if full_name is None: |
| 101 | full_name = ('net.proto2.python.public.proto_builder.AnonymousProto_' + |
| 102 | fields_hash.hexdigest()) |
| 103 | try: |
| 104 | proto_cls = _GetMessageFromFactory(factory, full_name) |
| 105 | return proto_cls |
| 106 | except KeyError: |
| 107 | # The factory's DescriptorPool doesn't know about this class yet. |
| 108 | pass |
| 109 | |
| 110 | # This is the first time we see this proto: add a new descriptor to the pool. |
| 111 | factory.pool.Add( |
| 112 | _MakeFileDescriptorProto(proto_file_name, full_name, field_items)) |
| 113 | return _GetMessageFromFactory(factory, full_name) |
| 114 | |
| 115 | |
| 116 | def _MakeFileDescriptorProto(proto_file_name, full_name, field_items): |
| 117 | """Populate FileDescriptorProto for MessageFactory's DescriptorPool.""" |
| 118 | package, name = full_name.rsplit('.', 1) |
| 119 | file_proto = descriptor_pb2.FileDescriptorProto() |
| 120 | file_proto.name = os.path.join(package.replace('.', '/'), proto_file_name) |
| 121 | file_proto.package = package |
| 122 | desc_proto = file_proto.message_type.add() |
| 123 | desc_proto.name = name |
| 124 | for f_number, (f_name, f_type) in enumerate(field_items, 1): |
| 125 | field_proto = desc_proto.field.add() |
| 126 | field_proto.name = f_name |
| 127 | field_proto.number = f_number |
| 128 | field_proto.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL |
| 129 | field_proto.type = f_type |
| 130 | return file_proto |