Merge pull request #221 from google/python_proto3
Proto3 Python changes for v3.0.0-alpha-2pull/222/head
commit
f8e7a46bc2
43 changed files with 6256 additions and 2121 deletions
@ -0,0 +1,438 @@ |
||||
#! /usr/bin/python |
||||
# |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Adds support for parameterized tests to Python's unittest TestCase class. |
||||
|
||||
A parameterized test is a method in a test case that is invoked with different |
||||
argument tuples. |
||||
|
||||
A simple example: |
||||
|
||||
class AdditionExample(parameterized.ParameterizedTestCase): |
||||
@parameterized.Parameters( |
||||
(1, 2, 3), |
||||
(4, 5, 9), |
||||
(1, 1, 3)) |
||||
def testAddition(self, op1, op2, result): |
||||
self.assertEquals(result, op1 + op2) |
||||
|
||||
|
||||
Each invocation is a separate test case and properly isolated just |
||||
like a normal test method, with its own setUp/tearDown cycle. In the |
||||
example above, there are three separate testcases, one of which will |
||||
fail due to an assertion error (1 + 1 != 3). |
||||
|
||||
Parameters for invididual test cases can be tuples (with positional parameters) |
||||
or dictionaries (with named parameters): |
||||
|
||||
class AdditionExample(parameterized.ParameterizedTestCase): |
||||
@parameterized.Parameters( |
||||
{'op1': 1, 'op2': 2, 'result': 3}, |
||||
{'op1': 4, 'op2': 5, 'result': 9}, |
||||
) |
||||
def testAddition(self, op1, op2, result): |
||||
self.assertEquals(result, op1 + op2) |
||||
|
||||
If a parameterized test fails, the error message will show the |
||||
original test name (which is modified internally) and the arguments |
||||
for the specific invocation, which are part of the string returned by |
||||
the shortDescription() method on test cases. |
||||
|
||||
The id method of the test, used internally by the unittest framework, |
||||
is also modified to show the arguments. To make sure that test names |
||||
stay the same across several invocations, object representations like |
||||
|
||||
>>> class Foo(object): |
||||
... pass |
||||
>>> repr(Foo()) |
||||
'<__main__.Foo object at 0x23d8610>' |
||||
|
||||
are turned into '<__main__.Foo>'. For even more descriptive names, |
||||
especially in test logs, you can use the NamedParameters decorator. In |
||||
this case, only tuples are supported, and the first parameters has to |
||||
be a string (or an object that returns an apt name when converted via |
||||
str()): |
||||
|
||||
class NamedExample(parameterized.ParameterizedTestCase): |
||||
@parameterized.NamedParameters( |
||||
('Normal', 'aa', 'aaa', True), |
||||
('EmptyPrefix', '', 'abc', True), |
||||
('BothEmpty', '', '', True)) |
||||
def testStartsWith(self, prefix, string, result): |
||||
self.assertEquals(result, strings.startswith(prefix)) |
||||
|
||||
Named tests also have the benefit that they can be run individually |
||||
from the command line: |
||||
|
||||
$ testmodule.py NamedExample.testStartsWithNormal |
||||
. |
||||
-------------------------------------------------------------------- |
||||
Ran 1 test in 0.000s |
||||
|
||||
OK |
||||
|
||||
Parameterized Classes |
||||
===================== |
||||
If invocation arguments are shared across test methods in a single |
||||
ParameterizedTestCase class, instead of decorating all test methods |
||||
individually, the class itself can be decorated: |
||||
|
||||
@parameterized.Parameters( |
||||
(1, 2, 3) |
||||
(4, 5, 9)) |
||||
class ArithmeticTest(parameterized.ParameterizedTestCase): |
||||
def testAdd(self, arg1, arg2, result): |
||||
self.assertEqual(arg1 + arg2, result) |
||||
|
||||
def testSubtract(self, arg2, arg2, result): |
||||
self.assertEqual(result - arg1, arg2) |
||||
|
||||
Inputs from Iterables |
||||
===================== |
||||
If parameters should be shared across several test cases, or are dynamically |
||||
created from other sources, a single non-tuple iterable can be passed into |
||||
the decorator. This iterable will be used to obtain the test cases: |
||||
|
||||
class AdditionExample(parameterized.ParameterizedTestCase): |
||||
@parameterized.Parameters( |
||||
c.op1, c.op2, c.result for c in testcases |
||||
) |
||||
def testAddition(self, op1, op2, result): |
||||
self.assertEquals(result, op1 + op2) |
||||
|
||||
|
||||
Single-Argument Test Methods |
||||
============================ |
||||
If a test method takes only one argument, the single argument does not need to |
||||
be wrapped into a tuple: |
||||
|
||||
class NegativeNumberExample(parameterized.ParameterizedTestCase): |
||||
@parameterized.Parameters( |
||||
-1, -3, -4, -5 |
||||
) |
||||
def testIsNegative(self, arg): |
||||
self.assertTrue(IsNegative(arg)) |
||||
""" |
||||
|
||||
__author__ = 'tmarek@google.com (Torsten Marek)' |
||||
|
||||
import collections |
||||
import functools |
||||
import re |
||||
import types |
||||
import unittest |
||||
import uuid |
||||
|
||||
from google.apputils import basetest |
||||
|
||||
ADDR_RE = re.compile(r'\<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+\>') |
||||
_SEPARATOR = uuid.uuid1().hex |
||||
_FIRST_ARG = object() |
||||
_ARGUMENT_REPR = object() |
||||
|
||||
|
||||
def _CleanRepr(obj): |
||||
return ADDR_RE.sub(r'<\1>', repr(obj)) |
||||
|
||||
|
||||
# Helper function formerly from the unittest module, removed from it in |
||||
# Python 2.7. |
||||
def _StrClass(cls): |
||||
return '%s.%s' % (cls.__module__, cls.__name__) |
||||
|
||||
|
||||
def _NonStringIterable(obj): |
||||
return (isinstance(obj, collections.Iterable) and not |
||||
isinstance(obj, basestring)) |
||||
|
||||
|
||||
def _FormatParameterList(testcase_params): |
||||
if isinstance(testcase_params, collections.Mapping): |
||||
return ', '.join('%s=%s' % (argname, _CleanRepr(value)) |
||||
for argname, value in testcase_params.iteritems()) |
||||
elif _NonStringIterable(testcase_params): |
||||
return ', '.join(map(_CleanRepr, testcase_params)) |
||||
else: |
||||
return _FormatParameterList((testcase_params,)) |
||||
|
||||
|
||||
class _ParameterizedTestIter(object): |
||||
"""Callable and iterable class for producing new test cases.""" |
||||
|
||||
def __init__(self, test_method, testcases, naming_type): |
||||
"""Returns concrete test functions for a test and a list of parameters. |
||||
|
||||
The naming_type is used to determine the name of the concrete |
||||
functions as reported by the unittest framework. If naming_type is |
||||
_FIRST_ARG, the testcases must be tuples, and the first element must |
||||
have a string representation that is a valid Python identifier. |
||||
|
||||
Args: |
||||
test_method: The decorated test method. |
||||
testcases: (list of tuple/dict) A list of parameter |
||||
tuples/dicts for individual test invocations. |
||||
naming_type: The test naming type, either _NAMED or _ARGUMENT_REPR. |
||||
""" |
||||
self._test_method = test_method |
||||
self.testcases = testcases |
||||
self._naming_type = naming_type |
||||
|
||||
def __call__(self, *args, **kwargs): |
||||
raise RuntimeError('You appear to be running a parameterized test case ' |
||||
'without having inherited from parameterized.' |
||||
'ParameterizedTestCase. This is bad because none of ' |
||||
'your test cases are actually being run.') |
||||
|
||||
def __iter__(self): |
||||
test_method = self._test_method |
||||
naming_type = self._naming_type |
||||
|
||||
def MakeBoundParamTest(testcase_params): |
||||
@functools.wraps(test_method) |
||||
def BoundParamTest(self): |
||||
if isinstance(testcase_params, collections.Mapping): |
||||
test_method(self, **testcase_params) |
||||
elif _NonStringIterable(testcase_params): |
||||
test_method(self, *testcase_params) |
||||
else: |
||||
test_method(self, testcase_params) |
||||
|
||||
if naming_type is _FIRST_ARG: |
||||
# Signal the metaclass that the name of the test function is unique |
||||
# and descriptive. |
||||
BoundParamTest.__x_use_name__ = True |
||||
BoundParamTest.__name__ += str(testcase_params[0]) |
||||
testcase_params = testcase_params[1:] |
||||
elif naming_type is _ARGUMENT_REPR: |
||||
# __x_extra_id__ is used to pass naming information to the __new__ |
||||
# method of TestGeneratorMetaclass. |
||||
# The metaclass will make sure to create a unique, but nondescriptive |
||||
# name for this test. |
||||
BoundParamTest.__x_extra_id__ = '(%s)' % ( |
||||
_FormatParameterList(testcase_params),) |
||||
else: |
||||
raise RuntimeError('%s is not a valid naming type.' % (naming_type,)) |
||||
|
||||
BoundParamTest.__doc__ = '%s(%s)' % ( |
||||
BoundParamTest.__name__, _FormatParameterList(testcase_params)) |
||||
if test_method.__doc__: |
||||
BoundParamTest.__doc__ += '\n%s' % (test_method.__doc__,) |
||||
return BoundParamTest |
||||
return (MakeBoundParamTest(c) for c in self.testcases) |
||||
|
||||
|
||||
def _IsSingletonList(testcases): |
||||
"""True iff testcases contains only a single non-tuple element.""" |
||||
return len(testcases) == 1 and not isinstance(testcases[0], tuple) |
||||
|
||||
|
||||
def _ModifyClass(class_object, testcases, naming_type): |
||||
assert not getattr(class_object, '_id_suffix', None), ( |
||||
'Cannot add parameters to %s,' |
||||
' which already has parameterized methods.' % (class_object,)) |
||||
class_object._id_suffix = id_suffix = {} |
||||
for name, obj in class_object.__dict__.items(): |
||||
if (name.startswith(unittest.TestLoader.testMethodPrefix) |
||||
and isinstance(obj, types.FunctionType)): |
||||
delattr(class_object, name) |
||||
methods = {} |
||||
_UpdateClassDictForParamTestCase( |
||||
methods, id_suffix, name, |
||||
_ParameterizedTestIter(obj, testcases, naming_type)) |
||||
for name, meth in methods.iteritems(): |
||||
setattr(class_object, name, meth) |
||||
|
||||
|
||||
def _ParameterDecorator(naming_type, testcases): |
||||
"""Implementation of the parameterization decorators. |
||||
|
||||
Args: |
||||
naming_type: The naming type. |
||||
testcases: Testcase parameters. |
||||
|
||||
Returns: |
||||
A function for modifying the decorated object. |
||||
""" |
||||
def _Apply(obj): |
||||
if isinstance(obj, type): |
||||
_ModifyClass( |
||||
obj, |
||||
list(testcases) if not isinstance(testcases, collections.Sequence) |
||||
else testcases, |
||||
naming_type) |
||||
return obj |
||||
else: |
||||
return _ParameterizedTestIter(obj, testcases, naming_type) |
||||
|
||||
if _IsSingletonList(testcases): |
||||
assert _NonStringIterable(testcases[0]), ( |
||||
'Single parameter argument must be a non-string iterable') |
||||
testcases = testcases[0] |
||||
|
||||
return _Apply |
||||
|
||||
|
||||
def Parameters(*testcases): |
||||
"""A decorator for creating parameterized tests. |
||||
|
||||
See the module docstring for a usage example. |
||||
Args: |
||||
*testcases: Parameters for the decorated method, either a single |
||||
iterable, or a list of tuples/dicts/objects (for tests |
||||
with only one argument). |
||||
|
||||
Returns: |
||||
A test generator to be handled by TestGeneratorMetaclass. |
||||
""" |
||||
return _ParameterDecorator(_ARGUMENT_REPR, testcases) |
||||
|
||||
|
||||
def NamedParameters(*testcases): |
||||
"""A decorator for creating parameterized tests. |
||||
|
||||
See the module docstring for a usage example. The first element of |
||||
each parameter tuple should be a string and will be appended to the |
||||
name of the test method. |
||||
|
||||
Args: |
||||
*testcases: Parameters for the decorated method, either a single |
||||
iterable, or a list of tuples. |
||||
|
||||
Returns: |
||||
A test generator to be handled by TestGeneratorMetaclass. |
||||
""" |
||||
return _ParameterDecorator(_FIRST_ARG, testcases) |
||||
|
||||
|
||||
class TestGeneratorMetaclass(type): |
||||
"""Metaclass for test cases with test generators. |
||||
|
||||
A test generator is an iterable in a testcase that produces callables. These |
||||
callables must be single-argument methods. These methods are injected into |
||||
the class namespace and the original iterable is removed. If the name of the |
||||
iterable conforms to the test pattern, the injected methods will be picked |
||||
up as tests by the unittest framework. |
||||
|
||||
In general, it is supposed to be used in conjuction with the |
||||
Parameters decorator. |
||||
""" |
||||
|
||||
def __new__(mcs, class_name, bases, dct): |
||||
dct['_id_suffix'] = id_suffix = {} |
||||
for name, obj in dct.items(): |
||||
if (name.startswith(unittest.TestLoader.testMethodPrefix) and |
||||
_NonStringIterable(obj)): |
||||
iterator = iter(obj) |
||||
dct.pop(name) |
||||
_UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator) |
||||
|
||||
return type.__new__(mcs, class_name, bases, dct) |
||||
|
||||
|
||||
def _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator): |
||||
"""Adds individual test cases to a dictionary. |
||||
|
||||
Args: |
||||
dct: The target dictionary. |
||||
id_suffix: The dictionary for mapping names to test IDs. |
||||
name: The original name of the test case. |
||||
iterator: The iterator generating the individual test cases. |
||||
""" |
||||
for idx, func in enumerate(iterator): |
||||
assert callable(func), 'Test generators must yield callables, got %r' % ( |
||||
func,) |
||||
if getattr(func, '__x_use_name__', False): |
||||
new_name = func.__name__ |
||||
else: |
||||
new_name = '%s%s%d' % (name, _SEPARATOR, idx) |
||||
assert new_name not in dct, ( |
||||
'Name of parameterized test case "%s" not unique' % (new_name,)) |
||||
dct[new_name] = func |
||||
id_suffix[new_name] = getattr(func, '__x_extra_id__', '') |
||||
|
||||
|
||||
class ParameterizedTestCase(basetest.TestCase): |
||||
"""Base class for test cases using the Parameters decorator.""" |
||||
__metaclass__ = TestGeneratorMetaclass |
||||
|
||||
def _OriginalName(self): |
||||
return self._testMethodName.split(_SEPARATOR)[0] |
||||
|
||||
def __str__(self): |
||||
return '%s (%s)' % (self._OriginalName(), _StrClass(self.__class__)) |
||||
|
||||
def id(self): # pylint: disable=invalid-name |
||||
"""Returns the descriptive ID of the test. |
||||
|
||||
This is used internally by the unittesting framework to get a name |
||||
for the test to be used in reports. |
||||
|
||||
Returns: |
||||
The test id. |
||||
""" |
||||
return '%s.%s%s' % (_StrClass(self.__class__), |
||||
self._OriginalName(), |
||||
self._id_suffix.get(self._testMethodName, '')) |
||||
|
||||
|
||||
def CoopParameterizedTestCase(other_base_class): |
||||
"""Returns a new base class with a cooperative metaclass base. |
||||
|
||||
This enables the ParameterizedTestCase to be used in combination |
||||
with other base classes that have custom metaclasses, such as |
||||
mox.MoxTestBase. |
||||
|
||||
Only works with metaclasses that do not override type.__new__. |
||||
|
||||
Example: |
||||
|
||||
import google3 |
||||
import mox |
||||
|
||||
from google3.testing.pybase import parameterized |
||||
|
||||
class ExampleTest(parameterized.CoopParameterizedTestCase(mox.MoxTestBase)): |
||||
... |
||||
|
||||
Args: |
||||
other_base_class: (class) A test case base class. |
||||
|
||||
Returns: |
||||
A new class object. |
||||
""" |
||||
metaclass = type( |
||||
'CoopMetaclass', |
||||
(other_base_class.__metaclass__, |
||||
TestGeneratorMetaclass), {}) |
||||
return metaclass( |
||||
'CoopParameterizedTestCase', |
||||
(other_base_class, ParameterizedTestCase), {}) |
@ -1,63 +0,0 @@ |
||||
#! /usr/bin/python |
||||
# |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Test that the api_implementation defaults are what we expect.""" |
||||
|
||||
import os |
||||
import sys |
||||
# Clear environment implementation settings before the google3 imports. |
||||
os.environ.pop('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION', None) |
||||
os.environ.pop('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION', None) |
||||
|
||||
# pylint: disable=g-import-not-at-top |
||||
from google.apputils import basetest |
||||
from google.protobuf.internal import api_implementation |
||||
|
||||
|
||||
class ApiImplementationDefaultTest(basetest.TestCase): |
||||
|
||||
if sys.version_info.major <= 2: |
||||
|
||||
def testThatPythonIsTheDefault(self): |
||||
"""If -DPYTHON_PROTO_*IMPL* was given at build time, this may fail.""" |
||||
self.assertEqual('python', api_implementation.Type()) |
||||
|
||||
else: |
||||
|
||||
def testThatCppApiV2IsTheDefault(self): |
||||
"""If -DPYTHON_PROTO_*IMPL* was given at build time, this may fail.""" |
||||
self.assertEqual('cpp', api_implementation.Type()) |
||||
self.assertEqual(2, api_implementation.Version()) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
basetest.main() |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,95 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Mappings and Sequences of descriptors.
|
||||
// They implement containers like fields_by_name, EnumDescriptor.values...
|
||||
// See descriptor_containers.cc for more description.
|
||||
#include <Python.h> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
|
||||
class Descriptor; |
||||
class FileDescriptor; |
||||
class EnumDescriptor; |
||||
class OneofDescriptor; |
||||
|
||||
namespace python { |
||||
|
||||
// Initialize the various types and objects.
|
||||
bool InitDescriptorMappingTypes(); |
||||
|
||||
// Each function below returns a Mapping, or a Sequence of descriptors.
|
||||
// They all return a new reference.
|
||||
|
||||
namespace message_descriptor { |
||||
PyObject* NewMessageFieldsByName(const Descriptor* descriptor); |
||||
PyObject* NewMessageFieldsByNumber(const Descriptor* descriptor); |
||||
PyObject* NewMessageFieldsSeq(const Descriptor* descriptor); |
||||
|
||||
PyObject* NewMessageNestedTypesSeq(const Descriptor* descriptor); |
||||
PyObject* NewMessageNestedTypesByName(const Descriptor* descriptor); |
||||
|
||||
PyObject* NewMessageEnumsByName(const Descriptor* descriptor); |
||||
PyObject* NewMessageEnumsSeq(const Descriptor* descriptor); |
||||
PyObject* NewMessageEnumValuesByName(const Descriptor* descriptor); |
||||
|
||||
PyObject* NewMessageExtensionsByName(const Descriptor* descriptor); |
||||
PyObject* NewMessageExtensionsSeq(const Descriptor* descriptor); |
||||
|
||||
PyObject* NewMessageOneofsByName(const Descriptor* descriptor); |
||||
PyObject* NewMessageOneofsSeq(const Descriptor* descriptor); |
||||
} // namespace message_descriptor
|
||||
|
||||
namespace enum_descriptor { |
||||
PyObject* NewEnumValuesByName(const EnumDescriptor* descriptor); |
||||
PyObject* NewEnumValuesByNumber(const EnumDescriptor* descriptor); |
||||
PyObject* NewEnumValuesSeq(const EnumDescriptor* descriptor); |
||||
} // namespace enum_descriptor
|
||||
|
||||
namespace oneof_descriptor { |
||||
PyObject* NewOneofFieldsSeq(const OneofDescriptor* descriptor); |
||||
} // namespace oneof_descriptor
|
||||
|
||||
namespace file_descriptor { |
||||
PyObject* NewFileMessageTypesByName(const FileDescriptor* descriptor); |
||||
|
||||
PyObject* NewFileEnumTypesByName(const FileDescriptor* descriptor); |
||||
|
||||
PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor); |
||||
|
||||
PyObject* NewFileDependencies(const FileDescriptor* descriptor); |
||||
PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor); |
||||
} // namespace file_descriptor
|
||||
|
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
@ -1,58 +0,0 @@ |
||||
#! /usr/bin/python |
||||
# |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Tests for google.protobuf.pyext behavior.""" |
||||
|
||||
__author__ = 'anuraag@google.com (Anuraag Agrawal)' |
||||
|
||||
import os |
||||
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' |
||||
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION'] = '2' |
||||
|
||||
# We must set the implementation version above before the google3 imports. |
||||
# pylint: disable=g-import-not-at-top |
||||
from google.apputils import basetest |
||||
from google.protobuf.internal import api_implementation |
||||
# Run all tests from the original module by putting them in our namespace. |
||||
# pylint: disable=wildcard-import |
||||
from google.protobuf.internal.descriptor_test import * |
||||
|
||||
|
||||
class ConfirmCppApi2Test(basetest.TestCase): |
||||
|
||||
def testImplementationSetting(self): |
||||
self.assertEqual('cpp', api_implementation.Type()) |
||||
self.assertEqual(2, api_implementation.Version()) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
basetest.main() |
@ -0,0 +1,370 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Implements the DescriptorPool, which collects all descriptors.
|
||||
|
||||
#include <Python.h> |
||||
|
||||
#include <google/protobuf/descriptor.pb.h> |
||||
#include <google/protobuf/pyext/descriptor_pool.h> |
||||
#include <google/protobuf/pyext/descriptor.h> |
||||
#include <google/protobuf/pyext/scoped_pyobject_ptr.h> |
||||
|
||||
#define C(str) const_cast<char*>(str) |
||||
|
||||
#if PY_MAJOR_VERSION >= 3 |
||||
#define PyString_FromStringAndSize PyUnicode_FromStringAndSize |
||||
#if PY_VERSION_HEX < 0x03030000 |
||||
#error "Python 3.0 - 3.2 are not supported." |
||||
#endif |
||||
#define PyString_AsStringAndSize(ob, charpp, sizep) \ |
||||
(PyUnicode_Check(ob)? \
|
||||
((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
|
||||
PyBytes_AsStringAndSize(ob, (charpp), (sizep))) |
||||
#endif |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace python { |
||||
|
||||
namespace cdescriptor_pool { |
||||
|
||||
PyDescriptorPool* NewDescriptorPool() { |
||||
PyDescriptorPool* cdescriptor_pool = PyObject_New( |
||||
PyDescriptorPool, &PyDescriptorPool_Type); |
||||
if (cdescriptor_pool == NULL) { |
||||
return NULL; |
||||
} |
||||
|
||||
// Build a DescriptorPool for messages only declared in Python libraries.
|
||||
// generated_pool() contains all messages linked in C++ libraries, and is used
|
||||
// as underlay.
|
||||
cdescriptor_pool->pool = new DescriptorPool(DescriptorPool::generated_pool()); |
||||
|
||||
// TODO(amauryfa): Rewrite the SymbolDatabase in C so that it uses the same
|
||||
// storage.
|
||||
cdescriptor_pool->classes_by_descriptor = |
||||
new PyDescriptorPool::ClassesByMessageMap(); |
||||
cdescriptor_pool->interned_descriptors = |
||||
new hash_map<const void*, PyObject *>(); |
||||
cdescriptor_pool->descriptor_options = |
||||
new hash_map<const void*, PyObject *>(); |
||||
|
||||
return cdescriptor_pool; |
||||
} |
||||
|
||||
static void Dealloc(PyDescriptorPool* self) { |
||||
typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator; |
||||
for (iterator it = self->classes_by_descriptor->begin(); |
||||
it != self->classes_by_descriptor->end(); ++it) { |
||||
Py_DECREF(it->second); |
||||
} |
||||
delete self->classes_by_descriptor; |
||||
delete self->interned_descriptors; // its references were borrowed.
|
||||
for (hash_map<const void*, PyObject*>::iterator it = |
||||
self->descriptor_options->begin(); |
||||
it != self->descriptor_options->end(); ++it) { |
||||
Py_DECREF(it->second); |
||||
} |
||||
delete self->descriptor_options; |
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); |
||||
} |
||||
|
||||
PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* arg) { |
||||
Py_ssize_t name_size; |
||||
char* name; |
||||
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
const Descriptor* message_descriptor = |
||||
self->pool->FindMessageTypeByName(string(name, name_size)); |
||||
|
||||
if (message_descriptor == NULL) { |
||||
PyErr_Format(PyExc_TypeError, "Couldn't find message %.200s", name); |
||||
return NULL; |
||||
} |
||||
|
||||
return PyMessageDescriptor_New(message_descriptor); |
||||
} |
||||
|
||||
// Add a message class to our database.
|
||||
const Descriptor* RegisterMessageClass( |
||||
PyDescriptorPool* self, PyObject *message_class, PyObject* descriptor) { |
||||
ScopedPyObjectPtr full_message_name( |
||||
PyObject_GetAttrString(descriptor, "full_name")); |
||||
Py_ssize_t name_size; |
||||
char* name; |
||||
if (PyString_AsStringAndSize(full_message_name, &name, &name_size) < 0) { |
||||
return NULL; |
||||
} |
||||
const Descriptor *message_descriptor = |
||||
self->pool->FindMessageTypeByName(string(name, name_size)); |
||||
if (!message_descriptor) { |
||||
PyErr_Format(PyExc_TypeError, "Could not find C++ descriptor for '%s'", |
||||
name); |
||||
return NULL; |
||||
} |
||||
Py_INCREF(message_class); |
||||
typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator; |
||||
std::pair<iterator, bool> ret = self->classes_by_descriptor->insert( |
||||
std::make_pair(message_descriptor, message_class)); |
||||
if (!ret.second) { |
||||
// Update case: DECREF the previous value.
|
||||
Py_DECREF(ret.first->second); |
||||
ret.first->second = message_class; |
||||
} |
||||
return message_descriptor; |
||||
} |
||||
|
||||
// Retrieve the message class added to our database.
|
||||
PyObject *GetMessageClass(PyDescriptorPool* self, |
||||
const Descriptor *message_descriptor) { |
||||
typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator; |
||||
iterator ret = self->classes_by_descriptor->find(message_descriptor); |
||||
if (ret == self->classes_by_descriptor->end()) { |
||||
PyErr_Format(PyExc_TypeError, "No message class registered for '%s'", |
||||
message_descriptor->full_name().c_str()); |
||||
return NULL; |
||||
} else { |
||||
return ret->second; |
||||
} |
||||
} |
||||
|
||||
PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* arg) { |
||||
Py_ssize_t name_size; |
||||
char* name; |
||||
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
const FieldDescriptor* field_descriptor = |
||||
self->pool->FindFieldByName(string(name, name_size)); |
||||
if (field_descriptor == NULL) { |
||||
PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", |
||||
name); |
||||
return NULL; |
||||
} |
||||
|
||||
return PyFieldDescriptor_New(field_descriptor); |
||||
} |
||||
|
||||
PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg) { |
||||
Py_ssize_t name_size; |
||||
char* name; |
||||
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
const FieldDescriptor* field_descriptor = |
||||
self->pool->FindExtensionByName(string(name, name_size)); |
||||
if (field_descriptor == NULL) { |
||||
PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", name); |
||||
return NULL; |
||||
} |
||||
|
||||
return PyFieldDescriptor_New(field_descriptor); |
||||
} |
||||
|
||||
PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg) { |
||||
Py_ssize_t name_size; |
||||
char* name; |
||||
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
const EnumDescriptor* enum_descriptor = |
||||
self->pool->FindEnumTypeByName(string(name, name_size)); |
||||
if (enum_descriptor == NULL) { |
||||
PyErr_Format(PyExc_TypeError, "Couldn't find enum %.200s", name); |
||||
return NULL; |
||||
} |
||||
|
||||
return PyEnumDescriptor_New(enum_descriptor); |
||||
} |
||||
|
||||
PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) { |
||||
Py_ssize_t name_size; |
||||
char* name; |
||||
if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
const OneofDescriptor* oneof_descriptor = |
||||
self->pool->FindOneofByName(string(name, name_size)); |
||||
if (oneof_descriptor == NULL) { |
||||
PyErr_Format(PyExc_TypeError, "Couldn't find oneof %.200s", name); |
||||
return NULL; |
||||
} |
||||
|
||||
return PyOneofDescriptor_New(oneof_descriptor); |
||||
} |
||||
|
||||
static PyMethodDef Methods[] = { |
||||
{ C("FindFieldByName"), |
||||
(PyCFunction)FindFieldByName, |
||||
METH_O, |
||||
C("Searches for a field descriptor by full name.") }, |
||||
{ C("FindExtensionByName"), |
||||
(PyCFunction)FindExtensionByName, |
||||
METH_O, |
||||
C("Searches for extension descriptor by full name.") }, |
||||
{NULL} |
||||
}; |
||||
|
||||
} // namespace cdescriptor_pool
|
||||
|
||||
PyTypeObject PyDescriptorPool_Type = { |
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0) |
||||
C("google.protobuf.internal." |
||||
"_message.DescriptorPool"), // tp_name
|
||||
sizeof(PyDescriptorPool), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
(destructor)cdescriptor_pool::Dealloc, // tp_dealloc
|
||||
0, // tp_print
|
||||
0, // tp_getattr
|
||||
0, // tp_setattr
|
||||
0, // tp_compare
|
||||
0, // tp_repr
|
||||
0, // tp_as_number
|
||||
0, // tp_as_sequence
|
||||
0, // tp_as_mapping
|
||||
0, // tp_hash
|
||||
0, // tp_call
|
||||
0, // tp_str
|
||||
0, // tp_getattro
|
||||
0, // tp_setattro
|
||||
0, // tp_as_buffer
|
||||
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||
C("A Descriptor Pool"), // tp_doc
|
||||
0, // tp_traverse
|
||||
0, // tp_clear
|
||||
0, // tp_richcompare
|
||||
0, // tp_weaklistoffset
|
||||
0, // tp_iter
|
||||
0, // tp_iternext
|
||||
cdescriptor_pool::Methods, // tp_methods
|
||||
0, // tp_members
|
||||
0, // tp_getset
|
||||
0, // tp_base
|
||||
0, // tp_dict
|
||||
0, // tp_descr_get
|
||||
0, // tp_descr_set
|
||||
0, // tp_dictoffset
|
||||
0, // tp_init
|
||||
0, // tp_alloc
|
||||
0, // tp_new
|
||||
PyObject_Del, // tp_free
|
||||
}; |
||||
|
||||
// The code below loads new Descriptors from a serialized FileDescriptorProto.
|
||||
|
||||
|
||||
// Collects errors that occur during proto file building to allow them to be
|
||||
// propagated in the python exception instead of only living in ERROR logs.
|
||||
class BuildFileErrorCollector : public DescriptorPool::ErrorCollector { |
||||
public: |
||||
BuildFileErrorCollector() : error_message(""), had_errors(false) {} |
||||
|
||||
void AddError(const string& filename, const string& element_name, |
||||
const Message* descriptor, ErrorLocation location, |
||||
const string& message) { |
||||
// Replicates the logging behavior that happens in the C++ implementation
|
||||
// when an error collector is not passed in.
|
||||
if (!had_errors) { |
||||
error_message += |
||||
("Invalid proto descriptor for file \"" + filename + "\":\n"); |
||||
} |
||||
// As this only happens on failure and will result in the program not
|
||||
// running at all, no effort is made to optimize this string manipulation.
|
||||
error_message += (" " + element_name + ": " + message + "\n"); |
||||
} |
||||
|
||||
string error_message; |
||||
bool had_errors; |
||||
}; |
||||
|
||||
PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { |
||||
char* message_type; |
||||
Py_ssize_t message_len; |
||||
|
||||
if (PyBytes_AsStringAndSize(serialized_pb, &message_type, &message_len) < 0) { |
||||
return NULL; |
||||
} |
||||
|
||||
FileDescriptorProto file_proto; |
||||
if (!file_proto.ParseFromArray(message_type, message_len)) { |
||||
PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!"); |
||||
return NULL; |
||||
} |
||||
|
||||
// If the file was already part of a C++ library, all its descriptors are in
|
||||
// the underlying pool. No need to do anything else.
|
||||
const FileDescriptor* generated_file = |
||||
DescriptorPool::generated_pool()->FindFileByName(file_proto.name()); |
||||
if (generated_file != NULL) { |
||||
return PyFileDescriptor_NewWithPb(generated_file, serialized_pb); |
||||
} |
||||
|
||||
BuildFileErrorCollector error_collector; |
||||
const FileDescriptor* descriptor = |
||||
GetDescriptorPool()->pool->BuildFileCollectingErrors(file_proto, |
||||
&error_collector); |
||||
if (descriptor == NULL) { |
||||
PyErr_Format(PyExc_TypeError, |
||||
"Couldn't build proto file into descriptor pool!\n%s", |
||||
error_collector.error_message.c_str()); |
||||
return NULL; |
||||
} |
||||
|
||||
return PyFileDescriptor_NewWithPb(descriptor, serialized_pb); |
||||
} |
||||
|
||||
static PyDescriptorPool* global_cdescriptor_pool = NULL; |
||||
|
||||
bool InitDescriptorPool() { |
||||
if (PyType_Ready(&PyDescriptorPool_Type) < 0) |
||||
return false; |
||||
|
||||
global_cdescriptor_pool = cdescriptor_pool::NewDescriptorPool(); |
||||
if (global_cdescriptor_pool == NULL) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
PyDescriptorPool* GetDescriptorPool() { |
||||
return global_cdescriptor_pool; |
||||
} |
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
@ -0,0 +1,152 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__ |
||||
#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__ |
||||
|
||||
#include <Python.h> |
||||
|
||||
#include <google/protobuf/stubs/hash.h> |
||||
#include <google/protobuf/descriptor.h> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace python { |
||||
|
||||
// Wraps operations to the global DescriptorPool which contains information
|
||||
// about all messages and fields.
|
||||
//
|
||||
// There is normally one pool per process. We make it a Python object only
|
||||
// because it contains many Python references.
|
||||
// TODO(amauryfa): See whether such objects can appear in reference cycles, and
|
||||
// consider adding support for the cyclic GC.
|
||||
//
|
||||
// "Methods" that interacts with this DescriptorPool are in the cdescriptor_pool
|
||||
// namespace.
|
||||
typedef struct PyDescriptorPool { |
||||
PyObject_HEAD |
||||
|
||||
DescriptorPool* pool; |
||||
|
||||
// Make our own mapping to retrieve Python classes from C++ descriptors.
|
||||
//
|
||||
// Descriptor pointers stored here are owned by the DescriptorPool above.
|
||||
// Python references to classes are owned by this PyDescriptorPool.
|
||||
typedef hash_map<const Descriptor*, PyObject*> ClassesByMessageMap; |
||||
ClassesByMessageMap* classes_by_descriptor; |
||||
|
||||
// Store interned descriptors, so that the same C++ descriptor yields the same
|
||||
// Python object. Objects are not immortal: this map does not own the
|
||||
// references, and items are deleted when the last reference to the object is
|
||||
// released.
|
||||
// This is enough to support the "is" operator on live objects.
|
||||
// All descriptors are stored here.
|
||||
hash_map<const void*, PyObject*>* interned_descriptors; |
||||
|
||||
// Cache the options for any kind of descriptor.
|
||||
// Descriptor pointers are owned by the DescriptorPool above.
|
||||
// Python objects are owned by the map.
|
||||
hash_map<const void*, PyObject*>* descriptor_options; |
||||
} PyDescriptorPool; |
||||
|
||||
|
||||
extern PyTypeObject PyDescriptorPool_Type; |
||||
|
||||
namespace cdescriptor_pool { |
||||
|
||||
// Builds a new DescriptorPool. Normally called only once per process.
|
||||
PyDescriptorPool* NewDescriptorPool(); |
||||
|
||||
// Looks up a message by name.
|
||||
// Returns a message Descriptor, or NULL if not found.
|
||||
const Descriptor* FindMessageTypeByName(PyDescriptorPool* self, |
||||
const string& name); |
||||
|
||||
// Registers a new Python class for the given message descriptor.
|
||||
// Returns the message Descriptor.
|
||||
// On error, returns NULL with a Python exception set.
|
||||
const Descriptor* RegisterMessageClass( |
||||
PyDescriptorPool* self, PyObject* message_class, PyObject* descriptor); |
||||
|
||||
// Retrieves the Python class registered with the given message descriptor.
|
||||
//
|
||||
// Returns a *borrowed* reference if found, otherwise returns NULL with an
|
||||
// exception set.
|
||||
PyObject* GetMessageClass(PyDescriptorPool* self, |
||||
const Descriptor* message_descriptor); |
||||
|
||||
// Looks up a message by name. Returns a PyMessageDescriptor corresponding to
|
||||
// the field on success, or NULL on failure.
|
||||
//
|
||||
// Returns a new reference.
|
||||
PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* name); |
||||
|
||||
// Looks up a field by name. Returns a PyFieldDescriptor corresponding to
|
||||
// the field on success, or NULL on failure.
|
||||
//
|
||||
// Returns a new reference.
|
||||
PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* name); |
||||
|
||||
// Looks up an extension by name. Returns a PyFieldDescriptor corresponding
|
||||
// to the field on success, or NULL on failure.
|
||||
//
|
||||
// Returns a new reference.
|
||||
PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg); |
||||
|
||||
// Looks up an enum type by name. Returns a PyEnumDescriptor corresponding
|
||||
// to the field on success, or NULL on failure.
|
||||
//
|
||||
// Returns a new reference.
|
||||
PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg); |
||||
|
||||
// Looks up a oneof by name. Returns a COneofDescriptor corresponding
|
||||
// to the oneof on success, or NULL on failure.
|
||||
//
|
||||
// Returns a new reference.
|
||||
PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg); |
||||
|
||||
} // namespace cdescriptor_pool
|
||||
|
||||
// Implement the Python "_BuildFile" method, it takes a serialized
|
||||
// FileDescriptorProto, and adds it to the C++ DescriptorPool.
|
||||
// It returns a new FileDescriptor object, or NULL when an exception is raised.
|
||||
PyObject* Python_BuildFile(PyObject* ignored, PyObject* args); |
||||
|
||||
// Retrieve the global descriptor pool owned by the _message module.
|
||||
PyDescriptorPool* GetDescriptorPool(); |
||||
|
||||
// Initialize objects used by this module.
|
||||
bool InitDescriptorPool(); |
||||
|
||||
} // namespace python
|
||||
} // namespace protobuf
|
||||
|
||||
} // namespace google
|
||||
#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__
|
File diff suppressed because it is too large
Load Diff
@ -1,56 +0,0 @@ |
||||
#! /usr/bin/python |
||||
# |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Tests for google.protobuf.message_factory.""" |
||||
|
||||
import os |
||||
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' |
||||
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION'] = '2' |
||||
|
||||
# We must set the implementation version above before the google3 imports. |
||||
# pylint: disable=g-import-not-at-top |
||||
from google.apputils import basetest |
||||
from google.protobuf.internal import api_implementation |
||||
# Run all tests from the original module by putting them in our namespace. |
||||
# pylint: disable=wildcard-import |
||||
from google.protobuf.internal.message_factory_test import * |
||||
|
||||
|
||||
class ConfirmCppApi2Test(basetest.TestCase): |
||||
|
||||
def testImplementationSetting(self): |
||||
self.assertEqual('cpp', api_implementation.Type()) |
||||
self.assertEqual(2, api_implementation.Version()) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
basetest.main() |
@ -1,94 +0,0 @@ |
||||
#! /usr/bin/python |
||||
# -*- coding: utf-8 -*- |
||||
# |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Unittest for reflection.py, which tests the generated C++ implementation.""" |
||||
|
||||
__author__ = 'jasonh@google.com (Jason Hsueh)' |
||||
|
||||
import os |
||||
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' |
||||
os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION'] = '2' |
||||
|
||||
from google.apputils import basetest |
||||
from google.protobuf.internal import api_implementation |
||||
from google.protobuf.internal import more_extensions_dynamic_pb2 |
||||
from google.protobuf.internal import more_extensions_pb2 |
||||
from google.protobuf.internal.reflection_test import * |
||||
|
||||
|
||||
class ReflectionCppTest(basetest.TestCase): |
||||
def testImplementationSetting(self): |
||||
self.assertEqual('cpp', api_implementation.Type()) |
||||
self.assertEqual(2, api_implementation.Version()) |
||||
|
||||
def testExtensionOfGeneratedTypeInDynamicFile(self): |
||||
"""Tests that a file built dynamically can extend a generated C++ type. |
||||
|
||||
The C++ implementation uses a DescriptorPool that has the generated |
||||
DescriptorPool as an underlay. Typically, a type can only find |
||||
extensions in its own pool. With the python C-extension, the generated C++ |
||||
extendee may be available, but not the extension. This tests that the |
||||
C-extension implements the correct special handling to make such extensions |
||||
available. |
||||
""" |
||||
pb1 = more_extensions_pb2.ExtendedMessage() |
||||
# Test that basic accessors work. |
||||
self.assertFalse( |
||||
pb1.HasExtension(more_extensions_dynamic_pb2.dynamic_int32_extension)) |
||||
self.assertFalse( |
||||
pb1.HasExtension(more_extensions_dynamic_pb2.dynamic_message_extension)) |
||||
pb1.Extensions[more_extensions_dynamic_pb2.dynamic_int32_extension] = 17 |
||||
pb1.Extensions[more_extensions_dynamic_pb2.dynamic_message_extension].a = 24 |
||||
self.assertTrue( |
||||
pb1.HasExtension(more_extensions_dynamic_pb2.dynamic_int32_extension)) |
||||
self.assertTrue( |
||||
pb1.HasExtension(more_extensions_dynamic_pb2.dynamic_message_extension)) |
||||
|
||||
# Now serialize the data and parse to a new message. |
||||
pb2 = more_extensions_pb2.ExtendedMessage() |
||||
pb2.MergeFromString(pb1.SerializeToString()) |
||||
|
||||
self.assertTrue( |
||||
pb2.HasExtension(more_extensions_dynamic_pb2.dynamic_int32_extension)) |
||||
self.assertTrue( |
||||
pb2.HasExtension(more_extensions_dynamic_pb2.dynamic_message_extension)) |
||||
self.assertEqual( |
||||
17, pb2.Extensions[more_extensions_dynamic_pb2.dynamic_int32_extension]) |
||||
self.assertEqual( |
||||
24, |
||||
pb2.Extensions[more_extensions_dynamic_pb2.dynamic_message_extension].a) |
||||
|
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
basetest.main() |
Binary file not shown.
Loading…
Reference in new issue