diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5b0c3e9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +sudo: required +dist: trusty +language: cpp + +before_install: + - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") + - sudo apt-get -qq update + - sudo apt-get install -y libboost-python-dev python-numpy python3-numpy libpython3-dev python-sphinx + - sudo ln -s /usr/lib/x86_64-linux-gnu/libboost_python-py34.so /usr/lib/x86_64-linux-gnu/libboost_python3.so + +script: + - mkdir build + - cd build + - cmake -DPYTHON_EXECUTABLE=$PYTHON .. && make && make test ARGS="-V" + - cd ../libs/numpy/doc + - make html + - cd ../../../ + - if [ "$PYTHON" == "/usr/bin/python" ]; then scons; fi + +env: + - PYTHON=/usr/bin/python + - PYTHON=/usr/bin/python3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8051673..8e4c74e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.3) +cmake_minimum_required(VERSION 2.8.12) project( boost.numpy ) @@ -43,6 +43,8 @@ find_package(NumPy REQUIRED) # set(Boost_USE_STATIC_LIBS ON) # set(Boost_USE_MULTITHREADED ON) # set(Boost_USE_STATIC_RUNTIME ON) +set(CMAKE_MACOSX_RPATH 1) + if(${PYTHON_VERSION_STRING} GREATER 3.0) message(STATUS "Using Python3") find_package(Boost COMPONENTS python3 REQUIRED) diff --git a/README.md b/README.md index 7e17a04..bdd00f2 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,25 @@ -# Boost.NumPy +# Boost.NumPy [![Build Status](https://travis-ci.org/ndarray/Boost.NumPy.svg?branch=master)](https://travis-ci.org/ndarray/Boost.NumPy) -Boost.NumPy is an extension for Boost.Python that adds NumPy support. - -It is intended as a replacement for the old numeric support in -Boost.Python proper, which is now out-of-date and does not support -C/C++ pointer access to the data. Because it builds against the NumPy -headers and hence has a dependency that Boost.Python does not, it is -intended to be built as a separate library. This is also not intended -to be a high-level C++ array library; it would be more accurate to -consider it a C++ NumPy API, with the intent of making the NumPy C-API -available in a form that is safer and more convenient for C++ users -(and particularly those using Boost.Python, of course). - -*THIS IS NOT AN OFFICIAL BOOST LIBRARY* - -...we're just calling it Boost.NumPy right now because that clearly -indicates that it's for Boost.Python and NumPy. We may -propose it for inclusion in Boost eventually, but that's not high -on the priority list for any of the original authors. +> # THIS PACKAGE IS NOW DEPRECATED IN FAVOR OF NUMPY SUPPORT INCLUDED DIRECTLY IN BOOST.PYTHON. +> +> NumPy support is scheduled to be included in Boost release 1.63, and is already available on +> the Boost.Python master branch (https://github.com/boostorg/python/tree/master). The interface +> is the same as Boost.Numpy, but in a different namespace. +> +> There are currently no plans for further development on this independent package. +Boost.NumPy is an extension for Boost.Python < v1.63 that adds NumPy support. ## INSTALLATION -We have provided three build systems: Boost.Build, SCons, and CMake -build system. At the moment, we recommend using SCons or CMake on -Linux and CMake on Windows. +We have provided two build systems: SCons and CMake build system. +We reconmmend using CMake for all new users, and for anyone using +Python 3 (SCons itself does not support Python 3, and our SCons +build is restricted to building against the version of Python that +executes SCons. + +Please see `libs/numpy/doc/cmakeBuild.rst` for more information on +building with CMake. Building with SCons should be as simple as running `scons` and `scons install`, but you may need to use the `--with-boost*` options (see @@ -35,9 +30,6 @@ by running git submodule update --init -Please see `libs/numpy/doc/cmakeBuild.rst` for more information on -building with CMake. - ## DOCUMENTATION diff --git a/SConsChecks b/SConsChecks index b2af1e0..fbdb46b 160000 --- a/SConsChecks +++ b/SConsChecks @@ -1 +1 @@ -Subproject commit b2af1e08ea4b887810184cef611572f11dbda8f7 +Subproject commit fbdb46be16969a7eba0afa7ac5f4639e9ae433d5 diff --git a/boost/numpy.hpp b/boost/numpy.hpp index e4fb087..df7cf64 100644 --- a/boost/numpy.hpp +++ b/boost/numpy.hpp @@ -26,11 +26,13 @@ namespace numpy { * This must be called before using anything in boost.numpy; * It should probably be the first line inside BOOST_PYTHON_MODULE. * + * This returns false and sets a Python exception on failure. + * * @internal This just calls the Numpy C-API functions "import_array()" * and "import_ufunc()", and then calls * dtype::register_scalar_converters(). */ -void initialize(bool register_scalar_converters=true); +bool initialize(bool register_scalar_converters=true); } // namespace boost::numpy } // namespace boost diff --git a/libs/numpy/doc/tutorial/dtype.rst b/libs/numpy/doc/tutorial/dtype.rst index 02bb513..c604b31 100644 --- a/libs/numpy/doc/tutorial/dtype.rst +++ b/libs/numpy/doc/tutorial/dtype.rst @@ -5,32 +5,32 @@ Here is a brief tutorial to show how to create ndarrays with built-in python dat Like before, first get the necessary headers, setup the namespaces and initialize the Python runtime and numpy module:: - #include - #include + #include + #include - namespace p = boost::python; - namespace np = boost::numpy; + namespace p = boost::python; + namespace np = boost::numpy; - int main(int argc, char **argv) - { - Py_Initialize(); - np::initialize(); + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); Next, we create the shape and dtype. We use the get_builtin method to get the numpy dtype corresponding to the builtin C++ dtype Here, we will create a 3x3 array passing a tuple with (3,3) for the size, and double as the data type :: - p::tuple shape = p::make_tuple(3, 3); - np::dtype dtype = np::dtype::get_builtin(); - np::ndarray a = np::zeros(shape, dtype); + p::tuple shape = p::make_tuple(3, 3); + np::dtype dtype = np::dtype::get_builtin(); + np::ndarray a = np::zeros(shape, dtype); Finally, we can print the array using the extract method in the python namespace. Here, we first convert the variable into a string, and then extract it as a C++ character array from the python string using the template :: - std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; + std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; We can also print the dtypes of the data members of the ndarray by using the get_dtype method for the ndarray :: - std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; + std::cout << "Datatype is:\n" << p::extract(p::str(a.get_dtype())) << std::endl ; We can also create custom dtypes and build ndarrays with the custom dtypes @@ -52,4 +52,4 @@ We are now ready to create an ndarray with dimensions specified by \*shape\* and np::ndarray new_array = np::zeros(shape,custom_dtype); - } + } diff --git a/libs/numpy/doc/tutorial/fromdata.rst b/libs/numpy/doc/tutorial/fromdata.rst index d2e59fa..993d5e4 100644 --- a/libs/numpy/doc/tutorial/fromdata.rst +++ b/libs/numpy/doc/tutorial/fromdata.rst @@ -6,16 +6,16 @@ The from_data method makes this possible. Like before, first get the necessary headers, setup the namespaces and initialize the Python runtime and numpy module:: - #include - #include + #include + #include - namespace p = boost::python; - namespace np = boost::numpy; + namespace p = boost::python; + namespace np = boost::numpy; - int main(int argc, char **argv) - { - Py_Initialize(); - np::initialize(); + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); Create an array in C++ , and pass the pointer to it to the from_data method to create an ndarray:: diff --git a/libs/numpy/doc/tutorial/ndarray.rst b/libs/numpy/doc/tutorial/ndarray.rst index 02e732c..a2aea58 100644 --- a/libs/numpy/doc/tutorial/ndarray.rst +++ b/libs/numpy/doc/tutorial/ndarray.rst @@ -8,16 +8,16 @@ This tutorial will introduce you to some of the ways in which you can create nda First, as before, initialise the necessary namepaces and runtimes :: - #include - #include + #include + #include - namespace p = boost::python; - namespace np = boost::numpy; + namespace p = boost::python; + namespace np = boost::numpy; - int main(int argc, char **argv) - { - Py_Initialize(); - np::initialize(); + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); Let's now create an ndarray from a simple tuple. We first create a tuple object, and then pass it to the array method, to generate the necessary tuple :: diff --git a/libs/numpy/doc/tutorial/simple.rst b/libs/numpy/doc/tutorial/simple.rst index 3c46296..f56a831 100644 --- a/libs/numpy/doc/tutorial/simple.rst +++ b/libs/numpy/doc/tutorial/simple.rst @@ -5,37 +5,37 @@ Let's start with a simple tutorial to create and modify arrays. Get the necessary headers for numpy components and set up necessary namespaces:: - #include - #include + #include + #include - namespace p = boost::python; - namespace np = boost::numpy; + namespace p = boost::python; + namespace np = boost::numpy; Initialise the Python runtime, and the numpy module. Failure to call these results in segmentation errors:: - int main(int argc, char **argv) - { - Py_Initialize(); - np::initialize(); + int main(int argc, char **argv) + { + Py_Initialize(); + np::initialize(); Zero filled n-dimensional arrays can be created using the shape and data-type of the array as a parameter. Here, the shape is 3x3 and the datatype is the built-in float type:: - p::tuple shape = p::make_tuple(3, 3); - np::dtype dtype = np::dtype::get_builtin(); - np::ndarray a = np::zeros(shape, dtype); + p::tuple shape = p::make_tuple(3, 3); + np::dtype dtype = np::dtype::get_builtin(); + np::ndarray a = np::zeros(shape, dtype); You can also create an empty array like this :: - np::ndarray b = np::empty(shape,dtype); - + np::ndarray b = np::empty(shape,dtype); + Print the original and reshaped array. The array a which is a list is first converted to a string, and each value in the list is extracted using extract< >:: - std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; + std::cout << "Original array:\n" << p::extract(p::str(a)) << std::endl; - // Reshape the array into a 1D array - a = a.reshape(p::make_tuple(9)); - // Print it again. - std::cout << "Reshaped array:\n" << p::extract(p::str(a)) << std::endl; - } + // Reshape the array into a 1D array + a = a.reshape(p::make_tuple(9)); + // Print it again. + std::cout << "Reshaped array:\n" << p::extract(p::str(a)) << std::endl; + } diff --git a/libs/numpy/src/dtype.cpp b/libs/numpy/src/dtype.cpp index 51fc916..83a2bd2 100644 --- a/libs/numpy/src/dtype.cpp +++ b/libs/numpy/src/dtype.cpp @@ -57,7 +57,9 @@ BUILTIN_INT_DTYPE(8); BUILTIN_INT_DTYPE(16); BUILTIN_INT_DTYPE(32); BUILTIN_INT_DTYPE(64); +#ifdef NPY_FLOAT16 BUILTIN_FLOAT_DTYPE(16); +#endif BUILTIN_FLOAT_DTYPE(32); BUILTIN_FLOAT_DTYPE(64); BUILTIN_COMPLEX_DTYPE(64); diff --git a/libs/numpy/src/numpy.cpp b/libs/numpy/src/numpy.cpp index 0cc3f5f..e5bc216 100644 --- a/libs/numpy/src/numpy.cpp +++ b/libs/numpy/src/numpy.cpp @@ -12,22 +12,40 @@ namespace boost namespace numpy { +// NumPy's import_* macros always return if they encounter an error, +// but in Python 2 there's no return value, while Python 3 returns +// null on an error. The machinery below produces the same behavior +// on both Python versions: initialize returns false with an exception +// raised if there is an exception. + +static #if PY_MAJOR_VERSION == 2 -static void wrap_import_array() { - import_array(); -} +void #else -static void * wrap_import_array() { - import_array(); -} +PyObject * #endif - -void initialize(bool register_scalar_converters) +wrap_imports() { - wrap_import_array(); + import_array(); import_ufunc(); +#if PY_MAJOR_VERSION > 2 + Py_RETURN_NONE; +#endif +} + +bool initialize(bool register_scalar_converters) +{ +#if PY_MAJOR_VERSION == 2 + wrap_imports(); + if (PyErr_Occurred()) return false; +#else + PyObject * r = wrap_imports(); + if (!r) return false; + Py_DECREF(r); +#endif if (register_scalar_converters) dtype::register_scalar_converters(); + return true; } } diff --git a/libs/numpy/test/dtype.py b/libs/numpy/test/dtype.py index a280cc3..c10d251 100644 --- a/libs/numpy/test/dtype.py +++ b/libs/numpy/test/dtype.py @@ -8,6 +8,7 @@ import dtype_mod import unittest import numpy +import sys class DtypeTestCase(unittest.TestCase): @@ -27,8 +28,9 @@ def testIntegers(self): self.assertEquivalent(fu(True), numpy.dtype(u)) self.assertEquivalent(fs(int(1)), numpy.dtype(s)) self.assertEquivalent(fu(int(1)), numpy.dtype(u)) - self.assertEquivalent(fs(long(1)), numpy.dtype(s)) - self.assertEquivalent(fu(long(1)), numpy.dtype(u)) + if sys.version_info[0] == 2: + self.assertEquivalent(fs(long(1)), numpy.dtype(s)) + self.assertEquivalent(fu(long(1)), numpy.dtype(u)) for name in ("bool_", "byte", "ubyte", "short", "ushort", "intc", "uintc"): t = getattr(numpy, name) ft = getattr(dtype_mod, "accept_%s" % name) @@ -37,7 +39,8 @@ def testIntegers(self): self.assertEquivalent(ft(True), numpy.dtype(t)) if name != "bool_": self.assertEquivalent(ft(int(1)), numpy.dtype(t)) - self.assertEquivalent(ft(long(1)), numpy.dtype(t)) + if sys.version_info[0] == 2: + self.assertEquivalent(ft(long(1)), numpy.dtype(t)) def testFloats(self): diff --git a/libs/numpy/test/runCmakeTest.sh.in b/libs/numpy/test/runCmakeTest.sh.in index efaa756..4ce2966 100755 --- a/libs/numpy/test/runCmakeTest.sh.in +++ b/libs/numpy/test/runCmakeTest.sh.in @@ -1,3 +1,3 @@ #!/bin/bash export PYTHONPATH=@CMAKE_LIBRARY_OUTPUT_DIRECTORY@:${PYTHONPATH} -python $1 \ No newline at end of file +@PYTHON_EXECUTABLE@ $1