Skip to content

Commit 1e6d3ac

Browse files
committed
numpy c api import fixed
1 parent 1c2f638 commit 1e6d3ac

File tree

8 files changed

+83
-27
lines changed

8 files changed

+83
-27
lines changed

docs/source/basic_usage.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Example 1: Use an algorithm of the C++ library on a numpy array inplace
1717
#include <numeric> // Standard library import for std::accumulate
1818
#include "pybind11/pybind11.h" // Pybind11 import to define Python bindings
1919
#include "xtensor/xmath.hpp" // xtensor import for the C++ universal functions
20+
#define FORCE_IMPORT_ARRAY // numpy C api loading
2021
#include "xtensor-python/pyarray.hpp" // Numpy bindings
2122
2223
double sum_of_sines(xt::pyarray<double> &m)
@@ -28,6 +29,7 @@ Example 1: Use an algorithm of the C++ library on a numpy array inplace
2829
2930
PYBIND11_PLUGIN(xtensor_python_test)
3031
{
32+
import_array() //this is actually a macro
3133
pybind11::module m("xtensor_python_test", "Test module for xtensor python bindings");
3234
3335
m.def("sum_of_sines", sum_of_sines,
@@ -64,6 +66,7 @@ Example 2: Create a universal function from a C++ scalar function
6466
.. code::
6567
6668
#include "pybind11/pybind11.h"
69+
#define FORCE_IMPORT_ARRAY
6770
#include "xtensor-python/pyvectorize.hpp"
6871
#include <numeric>
6972
#include <cmath>
@@ -77,6 +80,7 @@ Example 2: Create a universal function from a C++ scalar function
7780
7881
PYBIND11_PLUGIN(xtensor_python_test)
7982
{
83+
import_array()
8084
py::module m("xtensor_python_test", "Test module for xtensor python bindings");
8185
8286
m.def("vectorized_func", xt::pyvectorize(scalar_func), "");

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ This software is licensed under the BSD-3-Clause license. See the LICENSE file f
6161

6262
basic_usage
6363
array_tensor
64+
numpy_capi
6465
cookie_cutter
6566

6667
.. toctree::

docs/source/numpy_capi.rst

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
.. Copyright (c) 2016, Johan Mabille and Sylvain Corlay
2+
3+
Distributed under the terms of the BSD 3-Clause License.
4+
5+
The full license is in the file LICENSE, distributed with this software.
6+
7+
Importing numpy C API
8+
=====================
9+
10+
Importing the C API module of numpy requires more code than just including a header. ``xtensor-python`` simplifies a lot
11+
this import, however some actions are still required in the user code.
12+
13+
Extension module with a single file
14+
-----------------------------------
15+
16+
When writing an extension module that is self-contained in a single file, its author should pay attention to the following
17+
points:
18+
19+
- ``FORCE_IMPORT_ARRAY`` must be defined before including any header of ``xtensor-python``.
20+
- ``import_array()`` must be called in the function initializing the module.
21+
22+
Thus the basic skeleton of the module looks like:
23+
24+
.. code::
25+
26+
#include "pybind11/pybind11.h"
27+
#define FORCE_IMPORT_ARRAY
28+
#include "xgtensor-python/pyarray.hpp"
29+
30+
PYBIND11_PLUGIN(plugin_name)
31+
{
32+
import_array()
33+
pybind11::module m(//...
34+
//...
35+
return m.ptr();
36+
}
37+
38+
39+
Extension module with multiple files
40+
------------------------------------
41+
42+
If the extension module contain many source files that include ``xtensor-python`` header files, the previous points are still
43+
required. However, the source files that don't contain the initializing code of the module can directly include ``xtensor-python``
44+
header files.
45+
46+
47+
Using other extension modules
48+
-----------------------------
49+
50+
Including an header of ``xtensor-python`` actually defines ``PY_ARRAY_UNIQUE_SYMBOL`` to ``xtensor_python_ARRAY_API``. This might
51+
be problematic if you import another library that defines its own ``PY_ARRAY_UNIQUE_SYMBOL``, or if you define yours. If so,
52+
you can override the behavior of ``xtensor-python`` by explicitly defining ``PY_ARRAY_UNIQUE_SYMBOL`` prior to including any
53+
``stenxor-python`` header:
54+
55+
.. code::
56+
57+
// in every source file
58+
#define PY_ARRAY_UNIQUE_SYMBOL my_uniqe_array_api
59+
#include "xtensor-python/pyarray.hpp"
60+
61+
62+

include/xtensor-python/pycontainer.hpp

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,23 @@
1717
#include "pybind11/common.h"
1818
#include "pybind11/complex.h"
1919

20+
#ifndef FORCE_IMPORT_ARRAY
21+
#define NO_IMPORT_ARRAY
22+
#endif
23+
#ifndef PY_ARRAY_UNIQUE_SYMBOL
24+
#define PY_ARRAY_UNIQUE_SYMBOL xtensor_python_ARRAY_API
25+
#endif
2026
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
2127
#include "numpy/arrayobject.h"
2228

2329
#include "xtensor/xcontainer.hpp"
2430

2531
namespace xt
2632
{
27-
static bool is_numpy_imported = false;
28-
29-
class numpy_import
30-
{
31-
protected:
32-
33-
inline numpy_import()
34-
{
35-
if (!is_numpy_imported)
36-
{
37-
_import_array();
38-
is_numpy_imported = true;
39-
}
40-
}
41-
42-
numpy_import(const numpy_import&) = default;
43-
numpy_import(numpy_import&&) = default;
44-
numpy_import& operator=(const numpy_import&) = default;
45-
numpy_import& operator=(numpy_import&&) = default;
46-
};
4733

4834
template <class D>
4935
class pycontainer : public pybind11::object,
50-
public xcontainer<D>,
51-
private numpy_import
36+
public xcontainer<D>
5237
{
5338
public:
5439

test/main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@
88

99
#include <Python.h>
1010

11+
#include "pybind11/numpy.h"
12+
13+
#define FORCE_IMPORT_ARRAY
14+
#include "xtensor-python/pyarray.hpp"
15+
1116
#include "gtest/gtest.h"
17+
#include <iostream>
1218

1319
int main(int argc, char* argv[])
1420
{
1521
// Initialize all the things (google-test and Python interpreter)
1622
Py_Initialize();
23+
import_array()
1724
::testing::InitGoogleTest(&argc, argv);
1825

1926
// Run test suite

test/test_pyarray.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
****************************************************************************/
88

99
#include "gtest/gtest.h"
10-
1110
#include "test_common.hpp"
12-
1311
#include "xtensor-python/pyarray.hpp"
1412

1513
namespace xt

test/test_pytensor.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
****************************************************************************/
88

99
#include "gtest/gtest.h"
10-
1110
#include "test_common.hpp"
12-
1311
#include "xtensor-python/pytensor.hpp"
1412

1513
namespace xt

test_python/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
#include "xtensor/xmath.hpp"
1212
#include "xtensor/xarray.hpp"
13-
13+
#define FORCE_IMPORT_ARRAY
1414
#include "xtensor-python/pyarray.hpp"
1515
#include "xtensor-python/pyvectorize.hpp"
1616

@@ -51,6 +51,7 @@ int add(int i, int j)
5151

5252
PYBIND11_PLUGIN(xtensor_python_test)
5353
{
54+
import_array()
5455
py::module m("xtensor_python_test", "Test module for xtensor python bindings");
5556

5657
m.def("example1", example1);

0 commit comments

Comments
 (0)