1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc.  All rights reserved.
3# http://code.google.com/p/protobuf/
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.internal.cpp 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
96  # Must be consistent with the protocol-compiler code in
97  # proto2/compiler/internal/generator.*.
98  _DESCRIPTOR_KEY = 'DESCRIPTOR'
99
100  def __new__(cls, name, bases, dictionary):
101    """Custom allocation for runtime-generated class types.
102
103    We override __new__ because this is apparently the only place
104    where we can meaningfully set __slots__ on the class we're creating(?).
105    (The interplay between metaclasses and slots is not very well-documented).
106
107    Args:
108      name: Name of the class (ignored, but required by the
109        metaclass protocol).
110      bases: Base classes of the class we're constructing.
111        (Should be message.Message).  We ignore this field, but
112        it's required by the metaclass protocol
113      dictionary: The class dictionary of the class we're
114        constructing.  dictionary[_DESCRIPTOR_KEY] must contain
115        a Descriptor object describing this protocol message
116        type.
117
118    Returns:
119      Newly-allocated class.
120    """
121    descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
122    bases = _NewMessage(bases, descriptor, dictionary)
123    superclass = super(GeneratedProtocolMessageType, cls)
124
125    new_class = superclass.__new__(cls, name, bases, dictionary)
126    setattr(descriptor, '_concrete_class', new_class)
127    return new_class
128
129  def __init__(cls, name, bases, dictionary):
130    """Here we perform the majority of our work on the class.
131    We add enum getters, an __init__ method, implementations
132    of all Message methods, and properties for all fields
133    in the protocol type.
134
135    Args:
136      name: Name of the class (ignored, but required by the
137        metaclass protocol).
138      bases: Base classes of the class we're constructing.
139        (Should be message.Message).  We ignore this field, but
140        it's required by the metaclass protocol
141      dictionary: The class dictionary of the class we're
142        constructing.  dictionary[_DESCRIPTOR_KEY] must contain
143        a Descriptor object describing this protocol message
144        type.
145    """
146    descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
147    _InitMessage(descriptor, cls)
148    superclass = super(GeneratedProtocolMessageType, cls)
149    superclass.__init__(name, bases, dictionary)
150
151
152def ParseMessage(descriptor, byte_str):
153  """Generate a new Message instance from this Descriptor and a byte string.
154
155  Args:
156    descriptor: Protobuf Descriptor object
157    byte_str: Serialized protocol buffer byte string
158
159  Returns:
160    Newly created protobuf Message object.
161  """
162
163  class _ResultClass(message.Message):
164    __metaclass__ = GeneratedProtocolMessageType
165    DESCRIPTOR = descriptor
166
167  new_msg = _ResultClass()
168  new_msg.ParseFromString(byte_str)
169  return new_msg
170