// // Copyright 2016 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H #define PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H /// \file tf/pyContainerConversions.h /// Utilities for providing C++ <-> Python container support. /* * Adapted (modified) from original at http://cctbx.sourceforge.net * Original file: * cctbx/scitbx/include/scitbx/boost_python/container_conversions.h * License: * http://cvs.sourceforge.net/viewcvs.py/cctbx/cctbx/ * LICENSE.txt?rev=1.2&view=markup */ #include "pxr/pxr.h" #include "pxr/base/tf/refPtr.h" #include "pxr/base/tf/weakPtr.h" #include "pxr/base/tf/diagnostic.h" #include "pxr/base/tf/iterator.h" #include "pxr/base/tf/pyUtils.h" #include #include #include #include #include #include #include #include PXR_NAMESPACE_OPEN_SCOPE // Converter from vector to python list. template struct TfPySequenceToPython { static PyObject* convert(ContainerType const &c) { boost::python::list result; TF_FOR_ALL(i, c) { result.append(*i); } return boost::python::incref(result.ptr()); } }; template struct TfPyMapToPythonDict { static PyObject* convert(ContainerType const &c) { return boost::python::incref(TfPyCopyMapToDictionary(c).ptr()); } }; namespace TfPyContainerConversions { template struct to_tuple { static PyObject* convert(ContainerType const& a) { boost::python::list result; typedef typename ContainerType::const_iterator const_iter; for(const_iter p=a.begin();p!=a.end();p++) { result.append(boost::python::object(*p)); } return boost::python::incref(boost::python::tuple(result).ptr()); } }; template struct to_tuple > { static PyObject* convert(std::pair const& a) { boost::python::tuple result = boost::python::make_tuple(a.first, a.second); return boost::python::incref(result.ptr()); } }; struct default_policy { static bool check_convertibility_per_element() { return false; } template static bool check_size(boost::type, std::size_t sz) { return true; } template static void assert_size(boost::type, std::size_t sz) {} template static void reserve(ContainerType& a, std::size_t sz) {} }; struct fixed_size_policy { static bool check_convertibility_per_element() { return true; } template static bool check_size(boost::type, std::size_t sz) { return ContainerType::size() == sz; } template static void assert_size(boost::type, std::size_t sz) { if (!check_size(boost::type(), sz)) { PyErr_SetString(PyExc_RuntimeError, "Insufficient elements for fixed-size array."); boost::python::throw_error_already_set(); } } template static void reserve(ContainerType& a, std::size_t sz) { if (sz > ContainerType::size()) { PyErr_SetString(PyExc_RuntimeError, "Too many elements for fixed-size array."); boost::python::throw_error_already_set(); } } template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { reserve(a, i+1); a[i] = v; } }; struct variable_capacity_policy : default_policy { template static void reserve(ContainerType& a, std::size_t sz) { a.reserve(sz); } template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { TF_AXIOM(a.size() == i); a.push_back(v); } }; struct variable_capacity_all_items_convertible_policy : variable_capacity_policy { static bool check_convertibility_per_element() { return true; } }; struct fixed_capacity_policy : variable_capacity_policy { template static bool check_size(boost::type, std::size_t sz) { return ContainerType::max_size() >= sz; } }; struct linked_list_policy : default_policy { template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { a.push_back(v); } }; struct set_policy : default_policy { template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { a.insert(v); } }; template struct from_python_sequence { typedef typename ContainerType::value_type container_element_type; from_python_sequence() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { if (!( PyList_Check(obj_ptr) || PyTuple_Check(obj_ptr) || PySet_Check(obj_ptr) || PyFrozenSet_Check(obj_ptr) || PyIter_Check(obj_ptr) || PyRange_Check(obj_ptr) || ( !PyBytes_Check(obj_ptr) && !PyUnicode_Check(obj_ptr) && ( Py_TYPE(obj_ptr) == 0 || Py_TYPE(Py_TYPE(obj_ptr)) == 0 || Py_TYPE(Py_TYPE(obj_ptr))->tp_name == 0 || std::strcmp( Py_TYPE(Py_TYPE(obj_ptr))->tp_name, "Boost.Python.class") != 0) && PyObject_HasAttrString(obj_ptr, "__len__") && PyObject_HasAttrString(obj_ptr, "__getitem__")))) return 0; boost::python::handle<> obj_iter( boost::python::allow_null(PyObject_GetIter(obj_ptr))); if (!obj_iter.get()) { // must be convertible to an iterator PyErr_Clear(); return 0; } if (ConversionPolicy::check_convertibility_per_element()) { Py_ssize_t obj_size = PyObject_Length(obj_ptr); if (obj_size < 0) { // must be a measurable sequence PyErr_Clear(); return 0; } if (!ConversionPolicy::check_size( boost::type(), obj_size)) return 0; bool is_range = PyRange_Check(obj_ptr); std::size_t i=0; if (!all_elements_convertible(obj_iter, is_range, i)) return 0; if (!is_range) assert(i == (std::size_t)obj_size); } return obj_ptr; } // This loop factored out by Achim Domma to avoid Visual C++ // Internal Compiler Error. static bool all_elements_convertible( boost::python::handle<>& obj_iter, bool is_range, std::size_t& i) { for(;;i++) { boost::python::handle<> py_elem_hdl( boost::python::allow_null(PyIter_Next(obj_iter.get()))); if (PyErr_Occurred()) { PyErr_Clear(); return false; } if (!py_elem_hdl.get()) break; // end of iteration boost::python::object py_elem_obj(py_elem_hdl); boost::python::extract elem_proxy(py_elem_obj); if (!elem_proxy.check()) return false; if (is_range) break; // in a range all elements are of the same type } return true; } static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr)); void* storage = ( (boost::python::converter::rvalue_from_python_storage*) data)->storage.bytes; new (storage) ContainerType(); data->convertible = storage; ContainerType& result = *((ContainerType*)storage); std::size_t i=0; for(;;i++) { boost::python::handle<> py_elem_hdl( boost::python::allow_null(PyIter_Next(obj_iter.get()))); if (PyErr_Occurred()) boost::python::throw_error_already_set(); if (!py_elem_hdl.get()) break; // end of iteration boost::python::object py_elem_obj(py_elem_hdl); boost::python::extract elem_proxy(py_elem_obj); ConversionPolicy::set_value(result, i, elem_proxy()); } ConversionPolicy::assert_size(boost::type(), i); } }; template struct from_python_tuple_pair { typedef typename PairType::first_type first_type; typedef typename PairType::second_type second_type; from_python_tuple_pair() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { if (!PyTuple_Check(obj_ptr) || PyTuple_Size(obj_ptr) != 2) { return 0; } boost::python::extract e1(PyTuple_GetItem(obj_ptr, 0)); boost::python::extract e2(PyTuple_GetItem(obj_ptr, 1)); if (!e1.check() || !e2.check()) { return 0; } return obj_ptr; } static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { void* storage = ( (boost::python::converter::rvalue_from_python_storage*) data)->storage.bytes; boost::python::extract e1(PyTuple_GetItem(obj_ptr, 0)); boost::python::extract e2(PyTuple_GetItem(obj_ptr, 1)); new (storage) PairType(e1(), e2()); data->convertible = storage; } }; template struct to_tuple_mapping { to_tuple_mapping() { boost::python::to_python_converter< ContainerType, to_tuple >(); } }; template struct tuple_mapping : to_tuple_mapping { tuple_mapping() { from_python_sequence< ContainerType, ConversionPolicy>(); } }; template struct tuple_mapping_fixed_size { tuple_mapping_fixed_size() { tuple_mapping< ContainerType, fixed_size_policy>(); } }; template struct tuple_mapping_fixed_capacity { tuple_mapping_fixed_capacity() { tuple_mapping< ContainerType, fixed_capacity_policy>(); } }; template struct tuple_mapping_variable_capacity { tuple_mapping_variable_capacity() { tuple_mapping< ContainerType, variable_capacity_policy>(); } }; template struct tuple_mapping_set { tuple_mapping_set() { tuple_mapping< ContainerType, set_policy>(); } }; template struct tuple_mapping_pair { tuple_mapping_pair() { boost::python::to_python_converter< ContainerType, to_tuple >(); from_python_tuple_pair(); } }; } // namespace TfPyContainerConversions template void TfPyRegisterStlSequencesFromPython() { using namespace TfPyContainerConversions; from_python_sequence< std::vector, variable_capacity_all_items_convertible_policy>(); from_python_sequence< std::list, variable_capacity_all_items_convertible_policy>(); from_python_sequence< std::deque, variable_capacity_all_items_convertible_policy>(); } PXR_NAMESPACE_CLOSE_SCOPE #endif // PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H