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# This code is meant to work on Python 2.4 and above only.
32
33"""Contains a metaclass and helper functions used to create
34protocol message classes from Descriptor objects at runtime.
35
36Recall that a metaclass is the "type" of a class.
37(A class is to a metaclass what an instance is to a class.)
38
39In this case, we use the GeneratedProtocolMessageType metaclass
40to inject all the useful functionality into the classes
41output by the protocol compiler at compile-time.
42
43The upshot of all this is that the real implementation
44details for ALL pure-Python protocol buffers are *here in
45this file*.
46"""
47
48__author__ = 'robinson@google.com (Will Robinson)'
49
50
51from google.protobuf.internal import api_implementation
52from google.protobuf import descriptor as descriptor_mod
53from google.protobuf import message
54
55_FieldDescriptor = descriptor_mod.FieldDescriptor
56
57
58if api_implementation.Type() == 'cpp':
59  if api_implementation.Version() == 2:
60    from google.protobuf.pyext import cpp_message
61    _NewMessage = cpp_message.NewMessage
62    _InitMessage = cpp_message.InitMessage
63  else:
64    from google.protobuf.internal import cpp_message
65    _NewMessage = cpp_message.NewMessage
66    _InitMessage = cpp_message.InitMessage
67else:
68  from google.protobuf.internal import python_message
69  _NewMessage = python_message.NewMessage
70  _InitMessage = python_message.InitMessage
71
72
73class GeneratedProtocolMessageType(type):
74
75  """Metaclass for protocol message classes created at runtime from Descriptors.
76
77  We add implementations for all methods described in the Message class.  We
78  also create properties to allow getting/setting all fields in the protocol
79  message.  Finally, we create slots to prevent users from accidentally
80  "setting" nonexistent fields in the protocol message, which then wouldn't get
81  serialized / deserialized properly.
82
83  The protocol compiler currently uses this metaclass to create protocol
84  message classes at runtime.  Clients can also manually create their own
85  classes at runtime, as in this example:
86
87  mydescriptor = Descriptor(.....)
88  class MyProtoClass(Message):
89    __metaclass__ = GeneratedProtocolMessageType
90    DESCRIPTOR = mydescriptor
91  myproto_instance = MyProtoClass()
92  myproto.foo_field = 23
93  ...
94
95  The above example will not work for nested types. If you wish to include them,
96  use reflection.MakeClass() instead of manually instantiating the class in
97  order to create the appropriate class structure.
98  """
99
100  # Must be consistent with the protocol-compiler code in
101  # proto2/compiler/internal/generator.*.
102  _DESCRIPTOR_KEY = 'DESCRIPTOR'
103
104  def __new__(cls, name, bases, dictionary):
105    """Custom allocation for runtime-generated class types.
106
107    We override __new__ because this is apparently the only place
108    where we can meaningfully set __slots__ on the class we're creating(?).
109    (The interplay between metaclasses and slots is not very well-documented).
110
111    Args:
112      name: Name of the class (ignored, but required by the
113        metaclass protocol).
114      bases: Base classes of the class we're constructing.
115        (Should be message.Message).  We ignore this field, but
116        it's required by the metaclass protocol
117      dictionary: The class dictionary of the class we're
118        constructing.  dictionary[_DESCRIPTOR_KEY] must contain
119        a Descriptor object describing this protocol message
120        type.
121
122    Returns:
123      Newly-allocated class.
124    """
125    descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
126    bases = _NewMessage(bases, descriptor, dictionary)
127    superclass = super(GeneratedProtocolMessageType, cls)
128
129    new_class = superclass.__new__(cls, name, bases, dictionary)
130    setattr(descriptor, '_concrete_class', new_class)
131    return new_class
132
133  def __init__(cls, name, bases, dictionary):
134    """Here we perform the majority of our work on the class.
135    We add enum getters, an __init__ method, implementations
136    of all Message methods, and properties for all fields
137    in the protocol type.
138
139    Args:
140      name: Name of the class (ignored, but required by the
141        metaclass protocol).
142      bases: Base classes of the class we're constructing.
143        (Should be message.Message).  We ignore this field, but
144        it's required by the metaclass protocol
145      dictionary: The class dictionary of the class we're
146        constructing.  dictionary[_DESCRIPTOR_KEY] must contain
147        a Descriptor object describing this protocol message
148        type.
149    """
150    descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
151    _InitMessage(descriptor, cls)
152    superclass = super(GeneratedProtocolMessageType, cls)
153    superclass.__init__(name, bases, dictionary)
154
155
156def ParseMessage(descriptor, byte_str):
157  """Generate a new Message instance from this Descriptor and a byte string.
158
159  Args:
160    descriptor: Protobuf Descriptor object
161    byte_str: Serialized protocol buffer byte string
162
163  Returns:
164    Newly created protobuf Message object.
165  """
166  result_class = MakeClass(descriptor)
167  new_msg = result_class()
168  new_msg.ParseFromString(byte_str)
169  return new_msg
170
171
172def MakeClass(descriptor):
173  """Construct a class object for a protobuf described by descriptor.
174
175  Composite descriptors are handled by defining the new class as a member of the
176  parent class, recursing as deep as necessary.
177  This is the dynamic equivalent to:
178
179  class Parent(message.Message):
180    __metaclass__ = GeneratedProtocolMessageType
181    DESCRIPTOR = descriptor
182    class Child(message.Message):
183      __metaclass__ = GeneratedProtocolMessageType
184      DESCRIPTOR = descriptor.nested_types[0]
185
186  Sample usage:
187    file_descriptor = descriptor_pb2.FileDescriptorProto()
188    file_descriptor.ParseFromString(proto2_string)
189    msg_descriptor = descriptor.MakeDescriptor(file_descriptor.message_type[0])
190    msg_class = reflection.MakeClass(msg_descriptor)
191    msg = msg_class()
192
193  Args:
194    descriptor: A descriptor.Descriptor object describing the protobuf.
195  Returns:
196    The Message class object described by the descriptor.
197  """
198  attributes = {}
199  for name, nested_type in descriptor.nested_types_by_name.items():
200    attributes[name] = MakeClass(nested_type)
201
202  attributes[GeneratedProtocolMessageType._DESCRIPTOR_KEY] = descriptor
203
204  return GeneratedProtocolMessageType(str(descriptor.name), (message.Message,),
205                                      attributes)
206