diff options
Diffstat (limited to '')
-rw-r--r-- | src/boost/libs/python/src/converter/from_python.cpp | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/boost/libs/python/src/converter/from_python.cpp b/src/boost/libs/python/src/converter/from_python.cpp new file mode 100644 index 000000000..9678be1cb --- /dev/null +++ b/src/boost/libs/python/src/converter/from_python.cpp @@ -0,0 +1,303 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python/converter/from_python.hpp> +#include <boost/python/converter/registrations.hpp> +#include <boost/python/converter/rvalue_from_python_data.hpp> + +#include <boost/python/object/find_instance.hpp> + +#include <boost/python/handle.hpp> +#include <boost/python/detail/raw_pyobject.hpp> +#include <boost/python/cast.hpp> + +#include <vector> +#include <algorithm> + +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<T>, +// x.construct(source, y) constructs an object of type T +// in y.storage.bytes and then sets y.convertible == y.storage.bytes, +// or else throws an exception and has no effect. +BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( + PyObject* source + , registration const& converters) +{ + rvalue_from_python_stage1_data data; + + // First check to see if it's embedded in an extension class + // instance, as a special case. + data.convertible = objects::find_instance_impl(source, converters.target_type, converters.is_shared_ptr); + data.construct = 0; + if (!data.convertible) + { + for (rvalue_from_python_chain const* chain = converters.rvalue_chain; + chain != 0; + chain = chain->next) + { + void* r = chain->convertible(source); + if (r != 0) + { + data.convertible = r; + data.construct = chain->construct; + break; + } + } + } + return data; +} + +// 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<T> object, where T is the +// target type of the conversion. +// +// Requires: data.convertible == ®istered<T>::converters +// +BOOST_PYTHON_DECL void* rvalue_result_from_python( + PyObject* src, rvalue_from_python_stage1_data& data) +{ + // Retrieve the registration + // Cast in two steps for less-capable compilers + void const* converters_ = data.convertible; + registration const& converters = *static_cast<registration const*>(converters_); + + // Look for an eligible converter + data = rvalue_from_python_stage1(src, converters); + return rvalue_from_python_stage2(src, data, converters); +} + +BOOST_PYTHON_DECL void* rvalue_from_python_stage2( + PyObject* source, rvalue_from_python_stage1_data& data, registration const& converters) +{ + if (!data.convertible) + { + handle<> msg( +#if PY_VERSION_HEX >= 0x03000000 + ::PyUnicode_FromFormat +#else + ::PyString_FromFormat +#endif + ( + "No registered converter was able to produce a C++ rvalue of type %s from this Python object of type %s" + , converters.target_type.name() + , source->ob_type->tp_name + )); + + PyErr_SetObject(PyExc_TypeError, msg.get()); + throw_error_already_set(); + } + + // If a construct function was registered (i.e. we found an + // rvalue conversion), call it now. + if (data.construct != 0) + data.construct(source, &data); + + // Return the address of the resulting C++ object + return data.convertible; +} + +BOOST_PYTHON_DECL void* get_lvalue_from_python( + PyObject* source + , registration const& converters) +{ + // Check to see if it's embedded in a class instance + void* x = objects::find_instance_impl(source, converters.target_type); + if (x) + return x; + + lvalue_from_python_chain const* chain = converters.lvalue_chain; + for (;chain != 0; chain = chain->next) + { + void* r = chain->convert(source); + if (r != 0) + return r; + } + return 0; +} + +namespace +{ + // Prevent looping in implicit conversions. This could/should be + // much more efficient, but will work for now. + typedef std::vector<rvalue_from_python_chain const*> visited_t; + static visited_t visited; + + inline bool visit(rvalue_from_python_chain const* chain) + { + visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain); + if (p != visited.end() && *p == chain) + return false; + visited.insert(p, chain); + return true; + } + + // RAII class for managing global visited marks. + struct unvisit + { + unvisit(rvalue_from_python_chain const* chain) + : chain(chain) {} + + ~unvisit() + { + visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain); + assert(p != visited.end()); + visited.erase(p); + } + private: + rvalue_from_python_chain const* chain; + }; +} + + +BOOST_PYTHON_DECL bool implicit_rvalue_convertible_from_python( + PyObject* source + , registration const& converters) +{ + if (objects::find_instance_impl(source, converters.target_type)) + return true; + + rvalue_from_python_chain const* chain = converters.rvalue_chain; + + if (!visit(chain)) + return false; + + unvisit protect(chain); + + for (;chain != 0; chain = chain->next) + { + if (chain->convertible(source)) + return true; + } + + return false; +} + +namespace +{ + void throw_no_lvalue_from_python(PyObject* source, registration const& converters, char const* ref_type) + { + handle<> msg( +#if PY_VERSION_HEX >= 0x03000000 + ::PyUnicode_FromFormat +#else + ::PyString_FromFormat +#endif + ( + "No registered converter was able to extract a C++ %s to type %s" + " from this Python object of type %s" + , ref_type + , converters.target_type.name() + , source->ob_type->tp_name + )); + + PyErr_SetObject(PyExc_TypeError, msg.get()); + + throw_error_already_set(); + } + + void* lvalue_result_from_python( + PyObject* source + , registration const& converters + , char const* ref_type) + { + handle<> holder(source); + if (source->ob_refcnt <= 1) + { + handle<> msg( +#if PY_VERSION_HEX >= 0x3000000 + ::PyUnicode_FromFormat +#else + ::PyString_FromFormat +#endif + ( + "Attempt to return dangling %s to object of type: %s" + , ref_type + , converters.target_type.name())); + + PyErr_SetObject(PyExc_ReferenceError, msg.get()); + + throw_error_already_set(); + } + + void* result = get_lvalue_from_python(source, converters); + if (!result) + (throw_no_lvalue_from_python)(source, converters, ref_type); + return result; + } + +} + +BOOST_PYTHON_DECL void throw_no_pointer_from_python(PyObject* source, registration const& converters) +{ + (throw_no_lvalue_from_python)(source, converters, "pointer"); +} + +BOOST_PYTHON_DECL void throw_no_reference_from_python(PyObject* source, registration const& converters) +{ + (throw_no_lvalue_from_python)(source, converters, "reference"); +} + +BOOST_PYTHON_DECL void* reference_result_from_python( + PyObject* source + , registration const& converters) +{ + return (lvalue_result_from_python)(source, converters, "reference"); +} + +BOOST_PYTHON_DECL void* pointer_result_from_python( + PyObject* source + , registration const& converters) +{ + if (source == Py_None) + { + Py_DECREF(source); + return 0; + } + return (lvalue_result_from_python)(source, converters, "pointer"); +} + +BOOST_PYTHON_DECL void void_result_from_python(PyObject* o) +{ + Py_DECREF(expect_non_null(o)); +} + +} // namespace boost::python::converter + +BOOST_PYTHON_DECL PyObject* +pytype_check(PyTypeObject* type_, PyObject* source) +{ + if (!PyObject_IsInstance(source, python::upcast<PyObject>(type_))) + { + ::PyErr_Format( + PyExc_TypeError + , "Expecting an object of type %s; got an object of type %s instead" + , type_->tp_name + , source->ob_type->tp_name + ); + throw_error_already_set(); + } + return source; +} + +}} // namespace boost::python |