Fix containers of nullable<T> and blob in Python

Custom converters from a C++ type to a Python type are not invoked when
accessing container elements by reference (the default access policy).
This lead to an error when accessing elements of a container of
nullable<T> or blob. The fix is to return elements by value for types
that have a converter.
This commit is contained in:
Adam Sapek 2015-09-26 19:38:50 -07:00
Родитель be41d67a53
Коммит ec7e0eb39b
4 изменённых файлов: 61 добавлений и 12 удалений

Просмотреть файл

@ -35,6 +35,21 @@ namespace python
{
template <typename T> struct
has_custom_converter
: std::false_type
{};
template <typename T> struct
has_custom_converter<bond::nullable<T>>
: std::true_type
{};
template <> struct
has_custom_converter<bond::blob>
: std::true_type
{};
// Convert Bond type name to a valid python identifier
class pythonic_name
{

Просмотреть файл

@ -89,12 +89,15 @@ private:
typename boost::enable_if<is_list_container<T> >::type
def_type(T*) const
{
static const bool NoProxy
= has_custom_converter<typename element_type<T>::type>::value;
typedef typename boost::mpl::if_<
is_same<
typename std::iterator_traits<typename T::iterator>::iterator_category,
std::random_access_iterator_tag>,
boost::python::vector_indexing_suite<T>,
list_indexing_suite<T>
boost::python::vector_indexing_suite<T, NoProxy>,
list_indexing_suite<T, NoProxy>
>::type indexing_suite;
// Expose container class to Python
@ -114,7 +117,8 @@ private:
def_type(T*) const
{
static const bool NoProxy
= bond::is_string_type<typename element_type<T>::type::second_type>::value;
= bond::is_string_type<typename element_type<T>::type::second_type>::value
| has_custom_converter<typename element_type<T>::type::second_type>::value;
// Expose container class to Python
boost::python::class_<T>(bond::python::make_pythonic_name<T>())

Просмотреть файл

@ -79,17 +79,23 @@ struct NestedContainers
7: vector<vector<NestedWithBase>> vvNS;
8: vector<set<string>> vss;
11: vector<map<double, string>> vmds;
12: list<blob> lb;
13: vector<blob> vb;
14: map<string, blob> msb;
};
struct Nullable
{
2: nullable<list<float>> nullable_list;
3: nullable<SimpleStruct> nullable_struct;
4: nullable<map<int8, int8>> nullable_map;
5: nullable<string> nullable_string;
6: nullable<blob> nullable_blob;
10: nullable<nullable<uint32>> nullable_nullable_uint32;
2: nullable<list<float>> nullable_list;
3: nullable<SimpleStruct> nullable_struct;
4: nullable<map<int8, int8>> nullable_map;
5: nullable<string> nullable_string;
6: nullable<blob> nullable_blob;
10: nullable<nullable<uint32>> nullable_nullable_uint32;
11: list<nullable<SimpleStruct>> list_nullable_struct;
12: map<int8, nullable<float>> map_nullable_float;
13: vector<nullable<string>> vector_nullable_string;
};
struct Nothing

Просмотреть файл

@ -149,6 +149,15 @@ class BondTest(unittest.TestCase):
self.assertNotEqual(None, obj.nullable_string)
self.assertNotEqual(None, obj.nullable_blob)
self.assertNotEqual(None, obj.nullable_nullable_uint32)
obj.list_nullable_struct = [None, self.randomSimpleStruct()]
self.assertEqual(None, obj.list_nullable_struct[0])
self.assertNotEqual(None, obj.list_nullable_struct[1])
obj.map_nullable_float = dict([(0, None), (1, 3.14)])
self.assertEqual(None, obj.map_nullable_float[0])
self.assertNotEqual(None, obj.map_nullable_float[1])
obj.vector_nullable_string = [None, "str"]
self.assertEqual(None, obj.vector_nullable_string[0])
self.assertNotEqual(None, obj.vector_nullable_string[1])
def initNestedContainers(self, obj):
obj.lvls = random_list(\
@ -158,6 +167,11 @@ class BondTest(unittest.TestCase):
obj.vlSLS = random_list(\
functools.partial(random_list, \
self.randomSimpleContainers))
obj.vvNS = random_list(\
functools.partial(random_list, \
test.NestedWithBase))
obj.vss = random_list(\
functools.partial(random_set, \
random_string))
@ -165,6 +179,9 @@ class BondTest(unittest.TestCase):
functools.partial(random_map, \
random.random, \
random_string))
obj.lb = random_list(random_blob)
obj.vb = random_list(random_blob)
obj.msb = random_map(random_string, random_blob)
def initGeneric(self, obj):
self.initSimpleStruct(obj)
@ -191,6 +208,7 @@ class BondTest(unittest.TestCase):
self.assertTrue(obj == new_obj)
def list_operations(self, a):
self.assertTrue(len(a) != 0)
b = [a[i] for i in range(0, len(a))]
self.assertTrue(len(a)==len(b) and all(a[i] == b[i] for i in range(0, len(a))))
a.append(a[0])
@ -199,9 +217,6 @@ class BondTest(unittest.TestCase):
self.assertTrue(len(a)==len(b) and all(a[i] == b[i] for i in range(0, len(a))))
a.extend(b)
self.assertTrue(all(a[i] == a[i+len(a)//2] for i in range(0, len(a)//2)))
s1 = set(a)
s2 = set(b)
self.assertTrue(s1 - s2 == set())
del a[0:len(b)]
self.assertTrue(len(a)==len(b) and all(a[i] == b[i] for i in range(0, len(a))))
self.assertEqual(len(a), len(b))
@ -224,6 +239,9 @@ class BondTest(unittest.TestCase):
self.assertTrue(len(a)==len(b)-1 and all(b[i] in a for i in range(0, len(b)-1)))
self.assertFalse(b[-1] in a)
self.assertRaises(KeyError, a.remove, b[-1])
s1 = set(a)
s2 = set(b)
self.assertTrue(s1 - s2 == set())
a.clear()
self.assertTrue(len(a) == 0)
@ -281,6 +299,9 @@ class BondTest(unittest.TestCase):
self.assertEqual(None, obj.nullable_nullable_uint32)
self.serialization(obj, self.initNullable)
self.marshaling(obj, self.initNullable)
self.list_operations(obj.list_nullable_struct)
self.list_operations(obj.vector_nullable_string)
self.map_operations(obj.map_nullable_float)
with self.assertRaises(TypeError):
obj.nullable_list = "str"
with self.assertRaises(TypeError):
@ -300,6 +321,9 @@ class BondTest(unittest.TestCase):
obj = test.NestedContainers()
self.serialization(obj, self.initNestedContainers)
self.marshaling(obj, self.initNestedContainers)
self.list_operations(obj.lb)
self.list_operations(obj.vb)
self.map_operations(obj.msb)
def test_Generics(self):
obj = test.Generic_unittest_SimpleStruct_()