From 2bfeb20550e046fd17a095880f082edab96cb935 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Tue, 16 Jul 2002 11:45:10 +0000 Subject: [PATCH] Added type checking when converting some Python types from python as return values. [SVN r14478] --- .../boost/python/converter/from_python.hpp | 10 +-- .../converter/pytype_result_from_python.hpp | 18 +++++ .../python/converter/return_from_python.hpp | 11 ++-- include/boost/python/list.hpp | 3 +- include/boost/python/long.hpp | 3 +- src/converter/from_python.cpp | 65 +++++++++++++++++-- src/list.cpp | 44 ++++++++++--- src/object/inheritance.cpp | 3 + test/list.cpp | 6 ++ test/list.py | 30 ++++++++- 10 files changed, 165 insertions(+), 28 deletions(-) create mode 100644 include/boost/python/converter/pytype_result_from_python.hpp diff --git a/include/boost/python/converter/from_python.hpp b/include/boost/python/converter/from_python.hpp index ac8b4894..26373ce0 100644 --- a/include/boost/python/converter/from_python.hpp +++ b/include/boost/python/converter/from_python.hpp @@ -24,13 +24,13 @@ BOOST_PYTHON_DECL rvalue_from_python_chain const* implicit_conversion_chain( BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( PyObject* source, registration const&); -BOOST_PYTHON_DECL void* rvalue_from_python_stage2( - PyObject*, rvalue_from_python_stage1_data&, void* storage); +BOOST_PYTHON_DECL void* rvalue_result_from_python( + PyObject*, rvalue_from_python_stage1_data&); -BOOST_PYTHON_DECL void* reference_from_python(PyObject*, registration const&); -BOOST_PYTHON_DECL void* pointer_from_python(PyObject*, registration const&); +BOOST_PYTHON_DECL void* reference_result_from_python(PyObject*, registration const&); +BOOST_PYTHON_DECL void* pointer_result_from_python(PyObject*, registration const&); -BOOST_PYTHON_DECL void void_from_python(PyObject*); +BOOST_PYTHON_DECL void void_result_from_python(PyObject*); }}} // namespace boost::python::converter diff --git a/include/boost/python/converter/pytype_result_from_python.hpp b/include/boost/python/converter/pytype_result_from_python.hpp new file mode 100644 index 00000000..b1a13e89 --- /dev/null +++ b/include/boost/python/converter/pytype_result_from_python.hpp @@ -0,0 +1,18 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef PYTYPE_RESULT_FROM_PYTHON_DWA2002716_HPP +# define PYTYPE_RESULT_FROM_PYTHON_DWA2002716_HPP + +# include + +namespace boost { namespace python { namespace converter { + +BOOST_PYTHON_DECL python::detail::new_reference +pytype_result_from_python(PyTypeObject* type, PyObject* source); + +}}} // namespace boost::python::converter + +#endif // PYTYPE_RESULT_FROM_PYTHON_DWA2002716_HPP diff --git a/include/boost/python/converter/return_from_python.hpp b/include/boost/python/converter/return_from_python.hpp index 902612e2..8cdaf81d 100755 --- a/include/boost/python/converter/return_from_python.hpp +++ b/include/boost/python/converter/return_from_python.hpp @@ -77,7 +77,7 @@ struct return_from_python result_type operator()(PyObject* x) const { - converter::void_from_python(x); + (void_result_from_python)(x); # ifdef BOOST_NO_VOID_RETURNS return result_type(); # endif @@ -101,21 +101,24 @@ namespace detail inline typename return_rvalue_from_python::result_type return_rvalue_from_python::operator()(PyObject* obj) { - return *(T*)rvalue_from_python_stage2(obj, m_data.stage1, m_data.storage.bytes); + return *(T*) + (rvalue_result_from_python)(obj, m_data.stage1); } template inline T return_reference_from_python::operator()(PyObject* obj) const { return python::detail::void_ptr_to_reference( - reference_from_python(obj, registered::converters) + (reference_result_from_python)(obj, registered::converters) , (T(*)())0); } template inline T return_pointer_from_python::operator()(PyObject* obj) const { - return T(pointer_from_python(obj, registered_pointee::converters)); + return T( + (pointer_result_from_python)(obj, registered_pointee::converters) + ); } } diff --git a/include/boost/python/list.hpp b/include/boost/python/list.hpp index 3de6dedd..b7948b04 100644 --- a/include/boost/python/list.hpp +++ b/include/boost/python/list.hpp @@ -8,6 +8,7 @@ # include # include +# include namespace boost { namespace python { @@ -153,7 +154,7 @@ namespace converter result_type operator()(PyObject* x) const { - return list(python::detail::new_reference(x)); + return list((pytype_result_from_python)(&PyList_Type, x)); } }; } diff --git a/include/boost/python/long.hpp b/include/boost/python/long.hpp index 2dfebf65..1b47728e 100644 --- a/include/boost/python/long.hpp +++ b/include/boost/python/long.hpp @@ -8,6 +8,7 @@ # include # include +# include namespace boost { namespace python { @@ -91,7 +92,7 @@ namespace converter result_type operator()(PyObject* x) const { - return long_(python::detail::new_reference(x)); + return long_((pytype_result_from_python)(&PyLong_Type, x)); } }; } diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index b9dc8614..953bf946 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -8,11 +8,28 @@ #include #include #include +#include #include #include namespace boost { namespace python { namespace converter { +// rvalue_from_python_stage1 -- do the first stage of a conversion +// from a Python object to a C++ rvalue. +// +// source - the Python object to be converted +// converters - the registry entry for the target type T +// +// Postcondition: where x is the result, one of: +// +// 1. x.convertible == 0, indicating failure +// +// 2. x.construct == 0, x.convertible is the address of an object of +// type T. Indicates a successful lvalue conversion +// +// 3. where y is of type rvalue_from_python_data, +// x.construct(source, y) attempts to construct an object of type T +// in y. Indicates an rvalue converter was found BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( PyObject* source , registration const& converters) @@ -34,20 +51,36 @@ BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( return data; } -BOOST_PYTHON_DECL void* rvalue_from_python_stage2( - PyObject* src, rvalue_from_python_stage1_data& data, void* storage) +// rvalue_result_from_python -- return the address of a C++ object which +// can be used as the result of calling a Python function. +// +// src - the Python object to be converted +// +// data - a reference to the base part of a +// rvalue_from_python_data object, where T is the +// target type of the conversion. +// +// Requires: data.convertible == ®istered::converters +// +BOOST_PYTHON_DECL void* rvalue_result_from_python( + PyObject* src, rvalue_from_python_stage1_data& data) { + // Take possession of the source object. handle<> holder(src); + // Retrieve the registration + // Cast in two steps for less-capable compilers void const* converters_ = data.convertible; registration const& converters = *static_cast(converters_); + + // Look for an eligible converter data = rvalue_from_python_stage1(src, converters); if (!data.convertible) { handle<> msg( ::PyString_FromFormat( - "No registered converter was able to produce a C++ lvalue of type %s from this Python object of type %s" + "No registered converter was able to produce a C++ rvalue of type %s from this Python object of type %s" , converters.target_type.name() , src->ob_type->tp_name )); @@ -130,7 +163,7 @@ BOOST_PYTHON_DECL rvalue_from_python_chain const* implicit_conversion_chain( return chain; } -BOOST_PYTHON_DECL void* reference_from_python( +BOOST_PYTHON_DECL void* reference_result_from_python( PyObject* source , registration const& converters) { @@ -163,7 +196,7 @@ BOOST_PYTHON_DECL void* reference_from_python( return result; } -BOOST_PYTHON_DECL void* pointer_from_python( +BOOST_PYTHON_DECL void* pointer_result_from_python( PyObject* source , registration const& converters) { @@ -172,7 +205,7 @@ BOOST_PYTHON_DECL void* pointer_from_python( Py_DECREF(source); return 0; } - return reference_from_python(source, converters); + return reference_result_from_python(source, converters); } BOOST_PYTHON_DECL void throw_no_class_registered() @@ -183,9 +216,27 @@ BOOST_PYTHON_DECL void throw_no_class_registered() throw_error_already_set(); } -BOOST_PYTHON_DECL void void_from_python(PyObject* o) +BOOST_PYTHON_DECL void void_result_from_python(PyObject* o) { Py_DECREF(expect_non_null(o)); } +BOOST_PYTHON_DECL python::detail::new_reference +pytype_result_from_python(PyTypeObject* type, PyObject* source) +{ + if (!PyType_IsSubtype(source->ob_type, type)) + { + handle<> keeper(source); + handle<> msg( + ::PyString_FromFormat( + "Expecting a Python %s return type, but got an object of type %s instead" + , type + , source->ob_type->tp_name + )); + PyErr_SetObject(PyExc_TypeError, msg.get()); + throw_error_already_set(); + } + return python::detail::new_reference(source); +} + }}} // namespace boost::python::converter diff --git a/src/list.cpp b/src/list.cpp index 7931c899..ed7e675f 100644 --- a/src/list.cpp +++ b/src/list.cpp @@ -24,8 +24,15 @@ BOOST_PYTHON_DECL list::list(object_cref sequence) BOOST_PYTHON_DECL void list::append(object_cref x) { - if (PyList_Append(this->ptr(), x.ptr()) == -1) - throw_error_already_set(); + if (PyList_CheckExact(this->ptr())) + { + if (PyList_Append(this->ptr(), x.ptr()) == -1) + throw_error_already_set(); + } + else + { + this->attr("append")(x); + } } BOOST_PYTHON_DECL long list::count(object_cref value) const @@ -53,8 +60,15 @@ BOOST_PYTHON_DECL long list::index(object_cref value) const BOOST_PYTHON_DECL void list::insert(int index, object_cref item) { - if (PyList_Insert(this->ptr(), index, item.ptr()) == -1) - throw_error_already_set(); + if (PyList_CheckExact(this->ptr())) + { + if (PyList_Insert(this->ptr(), index, item.ptr()) == -1) + throw_error_already_set(); + } + else + { + this->attr("insert")(index, item); + } } 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) BOOST_PYTHON_DECL void list::reverse() { - if (PyList_Reverse(this->ptr()) == -1) - throw_error_already_set(); + if (PyList_CheckExact(this->ptr())) + { + if (PyList_Reverse(this->ptr()) == -1) + throw_error_already_set(); + } + else + { + this->attr("reverse")(); + } } BOOST_PYTHON_DECL void list::sort() { - if (PyList_Sort(this->ptr()) == -1) - throw_error_already_set(); + if (PyList_CheckExact(this->ptr())) + { + if (PyList_Sort(this->ptr()) == -1) + throw_error_already_set(); + } + else + { + this->attr("sort")(); + } } BOOST_PYTHON_DECL void list::sort(object_cref cmpfunc) diff --git a/src/object/inheritance.cpp b/src/object/inheritance.cpp index 6725b95d..d84e2b36 100644 --- a/src/object/inheritance.cpp +++ b/src/object/inheritance.cpp @@ -6,6 +6,9 @@ #include #include #include +#if defined(BOOST_MSVC) && _MSC_FULL_VER == 13102171 +# include +#endif #include #include #include diff --git a/test/list.cpp b/test/list.cpp index 4ad285b4..a5c9bc87 100644 --- a/test/list.cpp +++ b/test/list.cpp @@ -38,6 +38,11 @@ object apply_object_list(object f, list x) return f(x); } +list apply_list_list(object f, list x) +{ + return call(f.ptr(), x); +} + void append_object(list& x, object y) { x.append(y); @@ -126,6 +131,7 @@ BOOST_PYTHON_MODULE_INIT(list_ext) .def("listify", listify) .def("listify_string", listify_string) .def("apply_object_list", apply_object_list) + .def("apply_list_list", apply_list_list) .def("append_object", append_object) .def("append_list", append_list) diff --git a/test/list.py b/test/list.py index a7387e2d..0357b9a2 100644 --- a/test/list.py +++ b/test/list.py @@ -20,9 +20,17 @@ X(22) 5 is not convertible to a list ->>> try: apply_object_list(identity, 5) +>>> try: result = apply_object_list(identity, 5) ... except TypeError: pass -... else: print 'expected an exception' +... else: print 'expected an exception, got', result, 'instead' + +>>> assert apply_list_list(identity, letters) is letters + + 5 is not convertible to a list as a return value + +>>> try: result = apply_list_list(len, letters) +... except TypeError: pass +... else: print 'expected an exception, got', result, 'instead' >>> append_object(letters, '.') >>> letters @@ -38,6 +46,24 @@ X(22) >>> letters ['h', 'e', 'l', 'l', 'o', '.', [1, 2]] + Check that subclass functions are properly called + +>>> class mylist(list): +... def append(self, o): +... list.append(self, o) +... if not hasattr(self, 'nappends'): +... self.nappends = 1 +... else: +... self.nappends += 1 +... +>>> l2 = mylist() +>>> append_object(l2, 'hello') +>>> append_object(l2, 'world') +>>> l2 +['hello', 'world'] +>>> l2.nappends +2 + >>> def printer(*args): ... for x in args: print x, ... print