From 5ecb5da31fd2d74c7cf2f7b9e1f933eab56318a2 Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Sun, 13 Nov 2016 19:33:58 +0100 Subject: [PATCH] Use stable pybind11 v1.8.1 and backport required array APIs --- .appveyor.yml | 2 +- .travis.yml | 2 +- conda.recipe/meta.yaml | 2 +- include/xtensor-python/pyarray.hpp | 9 +- include/xtensor-python/pybind11_backport.hpp | 144 +++++++++++++++++++ test/setup.py | 2 +- 6 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 include/xtensor-python/pybind11_backport.hpp diff --git a/.appveyor.yml b/.appveyor.yml index 8a9099c..5d00283 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,7 +25,7 @@ install: - conda install pytest -c conda-forge - cd test - conda install xtensor pytest numpy -c conda-forge - - pip install git+https://github.com/pybind/pybind11.git + - pip install pybind11==1.8.1 - xcopy /S %APPVEYOR_BUILD_FOLDER%\include %MINICONDA%\include build_script: diff --git a/.travis.yml b/.travis.yml index a02b44b..320e0e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ install: - conda info -a - cd test - conda install xtensor pytest numpy -c conda-forge - - pip install git+https://github.com/pybind/pybind11.git + - pip install pybind11==1.8.1 - cp -r $TRAVIS_BUILD_DIR/include/* $HOME/miniconda/include/ script: diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 0a00b70..a992102 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -18,7 +18,7 @@ build: requirements: run: - xtensor - - pybind11 + - pybind11 ==1.8.1 test: commands: diff --git a/include/xtensor-python/pyarray.hpp b/include/xtensor-python/pyarray.hpp index 71a99d4..79f6d62 100644 --- a/include/xtensor-python/pyarray.hpp +++ b/include/xtensor-python/pyarray.hpp @@ -13,6 +13,7 @@ #include #include "pybind11/numpy.h" +#include "pybind11_backport.hpp" #include "xtensor/xexpression.hpp" #include "xtensor/xsemantic.hpp" @@ -21,7 +22,7 @@ namespace xt { - using pybind_array = pybind11::array; + using pybind_array = pybind11::backport::array; using buffer_info = pybind11::buffer_info; /*********************** @@ -543,9 +544,9 @@ namespace xt { return nullptr; } - auto& api = pybind11::detail::npy_api::get(); - PyObject *result = api.PyArray_FromAny_(ptr, pybind11::dtype::of().release().ptr(), 0, 0, - pybind11::detail::npy_api::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr); + API &api = lookup_api(); + PyObject *descr = api.PyArray_DescrFromType_(pybind11::detail::npy_format_descriptor::value); + PyObject *result = api.PyArray_FromAny_(ptr, descr, 0, 0, API::NPY_ENSURE_ARRAY_ | ExtraFlags, nullptr); if (!result) { PyErr_Clear(); diff --git a/include/xtensor-python/pybind11_backport.hpp b/include/xtensor-python/pybind11_backport.hpp new file mode 100644 index 0000000..6883d52 --- /dev/null +++ b/include/xtensor-python/pybind11_backport.hpp @@ -0,0 +1,144 @@ +/* + Backport from pybind11/numpy.h v2.0 prerelease. + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ +#pragma once + +#include +#include + +#include "pybind11/numpy.h" + +namespace pybind11 { namespace backport { + struct PyArray_Proxy { + PyObject_HEAD + char *data; + int nd; + ssize_t *dimensions; + ssize_t *strides; + PyObject *base; + PyObject *descr; + int flags; + }; + + struct PyArrayDescr_Proxy { + PyObject_HEAD + PyObject *typeobj; + char kind; + char type; + char byteorder; + char flags; + int type_num; + int elsize; + int alignment; + char *subarray; + PyObject *fields; + PyObject *names; + }; + +#ifndef PyArray_GET_ +# define PyArray_GET_(ptr, attr) \ + (reinterpret_cast<::pybind11::backport::PyArray_Proxy*>(ptr)->attr) +#endif +#ifndef PyArrayDescr_GET_ +# define PyArrayDescr_GET_(ptr, attr) \ + (reinterpret_cast<::pybind11::backport::PyArrayDescr_Proxy*>(ptr)->attr) +#endif + + class array : public pybind11::array { + public: + using size_type = std::size_t; + + using pybind11::array::array; + + array() = default; + + template array(const std::vector& shape, + const std::vector& strides, + const T* ptr, handle base = {}) + : array(buffer_info(const_cast(ptr), sizeof(T), format_descriptor::value, + shape.size(), shape, strides)) { + if (base) throw std::runtime_error("array base is not supported yet"); + } + + template array(const std::vector &shape, + const T *ptr, handle base = {}) + : array(shape, default_strides(shape, sizeof(T)), ptr, base) { } + + template array(size_type size, const T *ptr, handle base) + : array(std::vector{size}, ptr) { } + + size_type size() const { + return std::accumulate(shape(), shape() + ndim(), size_type{1}, std::multiplies()); + } + + size_type itemsize() const { + return static_cast(PyArrayDescr_GET_(PyArray_GET_(m_ptr, descr), elsize)); + } + + size_type ndim() const { + return static_cast(PyArray_GET_(m_ptr, nd)); + } + + const size_type* shape() const { + return reinterpret_cast(PyArray_GET_(m_ptr, dimensions)); + } + + const size_type* strides() const { + return reinterpret_cast(PyArray_GET_(m_ptr, strides)); + } + + template void* data() { + return static_cast(PyArray_GET_(m_ptr, data)); + } + + template void* mutable_data() { + // check_writeable(); + return static_cast(PyArray_GET_(m_ptr, data)); + } + + template size_type offset_at(Ix... index) const { + if (sizeof...(index) > ndim()) + fail_dim_check(sizeof...(index), "too many indices for an array"); + return get_byte_offset(index...); + } + + size_type offset_at() const { return 0; } + + protected: + void fail_dim_check(size_type dim, const std::string& msg) const { + throw index_error(msg + ": " + std::to_string(dim) + + " (ndim = " + std::to_string(ndim()) + ")"); + } + + template size_type get_byte_offset(Ix... index) const { + const size_type idx[] = { static_cast(index)... }; + if (!std::equal(idx + 0, idx + sizeof...(index), shape(), std::less{})) { + auto mismatch = std::mismatch(idx + 0, idx + sizeof...(index), shape(), std::less{}); + throw index_error(std::string("index ") + std::to_string(*mismatch.first) + + " is out of bounds for axis " + std::to_string(mismatch.first - idx) + + " with size " + std::to_string(*mismatch.second)); + } + return std::inner_product(idx + 0, idx + sizeof...(index), strides(), size_type{0}); + } + + size_type get_byte_offset() const { return 0; } + + static std::vector default_strides(const std::vector& shape, + size_type itemsize) { + auto ndim = shape.size(); + std::vector strides(ndim); + if (ndim) { + std::fill(strides.begin(), strides.end(), itemsize); + for (size_type i = 0; i < ndim - 1; i++) + for (size_type j = 0; j < ndim - 1 - i; j++) + strides[j] *= shape[ndim - 1 - i]; + } + return strides; + } + }; +}} // namespace pybind11::backport diff --git a/test/setup.py b/test/setup.py index f54de2e..482ab31 100644 --- a/test/setup.py +++ b/test/setup.py @@ -94,7 +94,7 @@ def build_extensions(self): description='An example project using xtensor-python', long_description='', ext_modules=ext_modules, - install_requires=['pybind11>=1.8.1'], + install_requires=['pybind11==1.8.1'], cmdclass={'build_ext': BuildExt}, zip_safe=False, )