Skip to content

Commit 2bfeb20

Browse files
committed
Added type checking when converting some Python types from python as return values.
[SVN r14478]
1 parent fa77903 commit 2bfeb20

File tree

10 files changed

+165
-28
lines changed

10 files changed

+165
-28
lines changed

include/boost/python/converter/from_python.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ BOOST_PYTHON_DECL rvalue_from_python_chain const* implicit_conversion_chain(
2424
BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1(
2525
PyObject* source, registration const&);
2626

27-
BOOST_PYTHON_DECL void* rvalue_from_python_stage2(
28-
PyObject*, rvalue_from_python_stage1_data&, void* storage);
27+
BOOST_PYTHON_DECL void* rvalue_result_from_python(
28+
PyObject*, rvalue_from_python_stage1_data&);
2929

30-
BOOST_PYTHON_DECL void* reference_from_python(PyObject*, registration const&);
31-
BOOST_PYTHON_DECL void* pointer_from_python(PyObject*, registration const&);
30+
BOOST_PYTHON_DECL void* reference_result_from_python(PyObject*, registration const&);
31+
BOOST_PYTHON_DECL void* pointer_result_from_python(PyObject*, registration const&);
3232

33-
BOOST_PYTHON_DECL void void_from_python(PyObject*);
33+
BOOST_PYTHON_DECL void void_result_from_python(PyObject*);
3434

3535

3636
}}} // namespace boost::python::converter
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef PYTYPE_RESULT_FROM_PYTHON_DWA2002716_HPP
7+
# define PYTYPE_RESULT_FROM_PYTHON_DWA2002716_HPP
8+
9+
# include <boost/python/detail/raw_pyobject.hpp>
10+
11+
namespace boost { namespace python { namespace converter {
12+
13+
BOOST_PYTHON_DECL python::detail::new_reference
14+
pytype_result_from_python(PyTypeObject* type, PyObject* source);
15+
16+
}}} // namespace boost::python::converter
17+
18+
#endif // PYTYPE_RESULT_FROM_PYTHON_DWA2002716_HPP

include/boost/python/converter/return_from_python.hpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ struct return_from_python<void>
7777

7878
result_type operator()(PyObject* x) const
7979
{
80-
converter::void_from_python(x);
80+
(void_result_from_python)(x);
8181
# ifdef BOOST_NO_VOID_RETURNS
8282
return result_type();
8383
# endif
@@ -101,21 +101,24 @@ namespace detail
101101
inline typename return_rvalue_from_python<T>::result_type
102102
return_rvalue_from_python<T>::operator()(PyObject* obj)
103103
{
104-
return *(T*)rvalue_from_python_stage2(obj, m_data.stage1, m_data.storage.bytes);
104+
return *(T*)
105+
(rvalue_result_from_python)(obj, m_data.stage1);
105106
}
106107

107108
template <class T>
108109
inline T return_reference_from_python<T>::operator()(PyObject* obj) const
109110
{
110111
return python::detail::void_ptr_to_reference(
111-
reference_from_python(obj, registered<T>::converters)
112+
(reference_result_from_python)(obj, registered<T>::converters)
112113
, (T(*)())0);
113114
}
114115

115116
template <class T>
116117
inline T return_pointer_from_python<T>::operator()(PyObject* obj) const
117118
{
118-
return T(pointer_from_python(obj, registered_pointee<T>::converters));
119+
return T(
120+
(pointer_result_from_python)(obj, registered_pointee<T>::converters)
121+
);
119122
}
120123
}
121124

include/boost/python/list.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
# include <boost/python/object.hpp>
1010
# include <boost/python/converter/pytype_arg_from_python.hpp>
11+
# include <boost/python/converter/pytype_result_from_python.hpp>
1112

1213
namespace boost { namespace python {
1314

@@ -153,7 +154,7 @@ namespace converter
153154

154155
result_type operator()(PyObject* x) const
155156
{
156-
return list(python::detail::new_reference(x));
157+
return list((pytype_result_from_python)(&PyList_Type, x));
157158
}
158159
};
159160
}

include/boost/python/long.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
# include <boost/python/object.hpp>
1010
# include <boost/python/converter/pytype_arg_from_python.hpp>
11+
# include <boost/python/converter/pytype_result_from_python.hpp>
1112

1213
namespace boost { namespace python {
1314

@@ -91,7 +92,7 @@ namespace converter
9192

9293
result_type operator()(PyObject* x) const
9394
{
94-
return long_(python::detail::new_reference(x));
95+
return long_((pytype_result_from_python)(&PyLong_Type, x));
9596
}
9697
};
9798
}

src/converter/from_python.cpp

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,28 @@
88
#include <boost/python/converter/registrations.hpp>
99
#include <boost/python/converter/rvalue_from_python_data.hpp>
1010
#include <boost/python/handle.hpp>
11+
#include <boost/python/detail/raw_pyobject.hpp>
1112
#include <vector>
1213
#include <algorithm>
1314

1415
namespace boost { namespace python { namespace converter {
1516

17+
// rvalue_from_python_stage1 -- do the first stage of a conversion
18+
// from a Python object to a C++ rvalue.
19+
//
20+
// source - the Python object to be converted
21+
// converters - the registry entry for the target type T
22+
//
23+
// Postcondition: where x is the result, one of:
24+
//
25+
// 1. x.convertible == 0, indicating failure
26+
//
27+
// 2. x.construct == 0, x.convertible is the address of an object of
28+
// type T. Indicates a successful lvalue conversion
29+
//
30+
// 3. where y is of type rvalue_from_python_data<T>,
31+
// x.construct(source, y) attempts to construct an object of type T
32+
// in y. Indicates an rvalue converter was found
1633
BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1(
1734
PyObject* source
1835
, registration const& converters)
@@ -34,20 +51,36 @@ BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1(
3451
return data;
3552
}
3653

37-
BOOST_PYTHON_DECL void* rvalue_from_python_stage2(
38-
PyObject* src, rvalue_from_python_stage1_data& data, void* storage)
54+
// rvalue_result_from_python -- return the address of a C++ object which
55+
// can be used as the result of calling a Python function.
56+
//
57+
// src - the Python object to be converted
58+
//
59+
// data - a reference to the base part of a
60+
// rvalue_from_python_data<T> object, where T is the
61+
// target type of the conversion.
62+
//
63+
// Requires: data.convertible == &registered<T>::converters
64+
//
65+
BOOST_PYTHON_DECL void* rvalue_result_from_python(
66+
PyObject* src, rvalue_from_python_stage1_data& data)
3967
{
68+
// Take possession of the source object.
4069
handle<> holder(src);
4170

71+
// Retrieve the registration
72+
// Cast in two steps for less-capable compilers
4273
void const* converters_ = data.convertible;
4374
registration const& converters = *static_cast<registration const*>(converters_);
75+
76+
// Look for an eligible converter
4477
data = rvalue_from_python_stage1(src, converters);
4578

4679
if (!data.convertible)
4780
{
4881
handle<> msg(
4982
::PyString_FromFormat(
50-
"No registered converter was able to produce a C++ lvalue of type %s from this Python object of type %s"
83+
"No registered converter was able to produce a C++ rvalue of type %s from this Python object of type %s"
5184
, converters.target_type.name()
5285
, src->ob_type->tp_name
5386
));
@@ -130,7 +163,7 @@ BOOST_PYTHON_DECL rvalue_from_python_chain const* implicit_conversion_chain(
130163
return chain;
131164
}
132165

133-
BOOST_PYTHON_DECL void* reference_from_python(
166+
BOOST_PYTHON_DECL void* reference_result_from_python(
134167
PyObject* source
135168
, registration const& converters)
136169
{
@@ -163,7 +196,7 @@ BOOST_PYTHON_DECL void* reference_from_python(
163196
return result;
164197
}
165198

166-
BOOST_PYTHON_DECL void* pointer_from_python(
199+
BOOST_PYTHON_DECL void* pointer_result_from_python(
167200
PyObject* source
168201
, registration const& converters)
169202
{
@@ -172,7 +205,7 @@ BOOST_PYTHON_DECL void* pointer_from_python(
172205
Py_DECREF(source);
173206
return 0;
174207
}
175-
return reference_from_python(source, converters);
208+
return reference_result_from_python(source, converters);
176209
}
177210

178211
BOOST_PYTHON_DECL void throw_no_class_registered()
@@ -183,9 +216,27 @@ BOOST_PYTHON_DECL void throw_no_class_registered()
183216
throw_error_already_set();
184217
}
185218

186-
BOOST_PYTHON_DECL void void_from_python(PyObject* o)
219+
BOOST_PYTHON_DECL void void_result_from_python(PyObject* o)
187220
{
188221
Py_DECREF(expect_non_null(o));
189222
}
190223

224+
BOOST_PYTHON_DECL python::detail::new_reference
225+
pytype_result_from_python(PyTypeObject* type, PyObject* source)
226+
{
227+
if (!PyType_IsSubtype(source->ob_type, type))
228+
{
229+
handle<> keeper(source);
230+
handle<> msg(
231+
::PyString_FromFormat(
232+
"Expecting a Python %s return type, but got an object of type %s instead"
233+
, type
234+
, source->ob_type->tp_name
235+
));
236+
PyErr_SetObject(PyExc_TypeError, msg.get());
237+
throw_error_already_set();
238+
}
239+
return python::detail::new_reference(source);
240+
}
241+
191242
}}} // namespace boost::python::converter

src/list.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,15 @@ BOOST_PYTHON_DECL list::list(object_cref sequence)
2424

2525
BOOST_PYTHON_DECL void list::append(object_cref x)
2626
{
27-
if (PyList_Append(this->ptr(), x.ptr()) == -1)
28-
throw_error_already_set();
27+
if (PyList_CheckExact(this->ptr()))
28+
{
29+
if (PyList_Append(this->ptr(), x.ptr()) == -1)
30+
throw_error_already_set();
31+
}
32+
else
33+
{
34+
this->attr("append")(x);
35+
}
2936
}
3037

3138
BOOST_PYTHON_DECL long list::count(object_cref value) const
@@ -53,8 +60,15 @@ BOOST_PYTHON_DECL long list::index(object_cref value) const
5360

5461
BOOST_PYTHON_DECL void list::insert(int index, object_cref item)
5562
{
56-
if (PyList_Insert(this->ptr(), index, item.ptr()) == -1)
57-
throw_error_already_set();
63+
if (PyList_CheckExact(this->ptr()))
64+
{
65+
if (PyList_Insert(this->ptr(), index, item.ptr()) == -1)
66+
throw_error_already_set();
67+
}
68+
else
69+
{
70+
this->attr("insert")(index, item);
71+
}
5872
}
5973

6074
BOOST_PYTHON_DECL void list::insert(object const& index, object_cref x)
@@ -87,14 +101,28 @@ BOOST_PYTHON_DECL void list::remove(object_cref value)
87101

88102
BOOST_PYTHON_DECL void list::reverse()
89103
{
90-
if (PyList_Reverse(this->ptr()) == -1)
91-
throw_error_already_set();
104+
if (PyList_CheckExact(this->ptr()))
105+
{
106+
if (PyList_Reverse(this->ptr()) == -1)
107+
throw_error_already_set();
108+
}
109+
else
110+
{
111+
this->attr("reverse")();
112+
}
92113
}
93114

94115
BOOST_PYTHON_DECL void list::sort()
95116
{
96-
if (PyList_Sort(this->ptr()) == -1)
97-
throw_error_already_set();
117+
if (PyList_CheckExact(this->ptr()))
118+
{
119+
if (PyList_Sort(this->ptr()) == -1)
120+
throw_error_already_set();
121+
}
122+
else
123+
{
124+
this->attr("sort")();
125+
}
98126
}
99127

100128
BOOST_PYTHON_DECL void list::sort(object_cref cmpfunc)

src/object/inheritance.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include <boost/python/object/inheritance.hpp>
77
#include <boost/python/type_id.hpp>
88
#include <boost/graph/breadth_first_search.hpp>
9+
#if defined(BOOST_MSVC) && _MSC_FULL_VER == 13102171
10+
# include <boost/graph/reverse_graph.hpp>
11+
#endif
912
#include <boost/graph/adjacency_list.hpp>
1013
#include <boost/graph/reverse_graph.hpp>
1114
#include <boost/property_map.hpp>

test/list.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ object apply_object_list(object f, list x)
3838
return f(x);
3939
}
4040

41+
list apply_list_list(object f, list x)
42+
{
43+
return call<list>(f.ptr(), x);
44+
}
45+
4146
void append_object(list& x, object y)
4247
{
4348
x.append(y);
@@ -126,6 +131,7 @@ BOOST_PYTHON_MODULE_INIT(list_ext)
126131
.def("listify", listify)
127132
.def("listify_string", listify_string)
128133
.def("apply_object_list", apply_object_list)
134+
.def("apply_list_list", apply_list_list)
129135

130136
.def("append_object", append_object)
131137
.def("append_list", append_list)

test/list.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@
2020
2121
5 is not convertible to a list
2222
23-
>>> try: apply_object_list(identity, 5)
23+
>>> try: result = apply_object_list(identity, 5)
2424
... except TypeError: pass
25-
... else: print 'expected an exception'
25+
... else: print 'expected an exception, got', result, 'instead'
26+
27+
>>> assert apply_list_list(identity, letters) is letters
28+
29+
5 is not convertible to a list as a return value
30+
31+
>>> try: result = apply_list_list(len, letters)
32+
... except TypeError: pass
33+
... else: print 'expected an exception, got', result, 'instead'
2634
2735
>>> append_object(letters, '.')
2836
>>> letters
@@ -38,6 +46,24 @@
3846
>>> letters
3947
['h', 'e', 'l', 'l', 'o', '.', [1, 2]]
4048
49+
Check that subclass functions are properly called
50+
51+
>>> class mylist(list):
52+
... def append(self, o):
53+
... list.append(self, o)
54+
... if not hasattr(self, 'nappends'):
55+
... self.nappends = 1
56+
... else:
57+
... self.nappends += 1
58+
...
59+
>>> l2 = mylist()
60+
>>> append_object(l2, 'hello')
61+
>>> append_object(l2, 'world')
62+
>>> l2
63+
['hello', 'world']
64+
>>> l2.nappends
65+
2
66+
4167
>>> def printer(*args):
4268
... for x in args: print x,
4369
... print

0 commit comments

Comments
 (0)