diff --git a/CMakeLists.txt b/CMakeLists.txt index c1a421a31..834ce732e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,7 @@ endif() option(GTSAM_INSTALL_MATLAB_TOOLBOX "Enable/Disable installation of matlab toolbox" OFF) option(GTSAM_INSTALL_CYTHON_TOOLBOX "Enable/Disable installation of Cython toolbox" OFF) option(GTSAM_BUILD_WRAP "Enable/Disable building of matlab/cython wrap utility (necessary for matlab/cython interface)" ON) +set(GTSAM_PYTHON_VERSION "Default" CACHE STRING "The version of python to build the cython wrapper or python module for (or Default)") # Check / set dependent variables for MATLAB wrapper if((GTSAM_INSTALL_MATLAB_TOOLBOX OR GTSAM_INSTALL_CYTHON_TOOLBOX) AND NOT GTSAM_BUILD_WRAP) @@ -554,6 +555,7 @@ endif() message(STATUS "Cython toolbox flags ") print_config_flag(${GTSAM_INSTALL_CYTHON_TOOLBOX} "Install Cython toolbox ") +message(STATUS " Python version : ${GTSAM_PYTHON_VERSION}") print_config_flag(${GTSAM_BUILD_WRAP} "Build Wrap ") message(STATUS "===============================================================") diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake index 23afb00e6..e5a32c30d 100644 --- a/cmake/FindCython.cmake +++ b/cmake/FindCython.cmake @@ -29,10 +29,15 @@ # Use the Cython executable that lives next to the Python executable # if it is a local installation. -find_package( PythonInterp ) +if(GTSAM_PYTHON_VERSION STREQUAL "Default") + find_package(PythonInterp) +else() + find_package(PythonInterp ${GTSAM_PYTHON_VERSION} EXACT) +endif() + if ( PYTHONINTERP_FOUND ) execute_process( COMMAND "${PYTHON_EXECUTABLE}" "-c" - "import Cython; print Cython.__path__" + "import Cython; print(Cython.__path__[0])" RESULT_VARIABLE RESULT OUTPUT_VARIABLE CYTHON_PATH OUTPUT_STRIP_TRAILING_WHITESPACE @@ -51,7 +56,7 @@ endif () # RESULT=0 means ok if ( NOT RESULT ) execute_process( COMMAND "${PYTHON_EXECUTABLE}" "-c" - "import Cython; print Cython.__version__" + "import Cython; print(Cython.__version__)" RESULT_VARIABLE RESULT OUTPUT_VARIABLE CYTHON_VAR_OUTPUT ERROR_VARIABLE CYTHON_VAR_OUTPUT diff --git a/cmake/FindNumPy.cmake b/cmake/FindNumPy.cmake index eafed165e..4f5743aa6 100644 --- a/cmake/FindNumPy.cmake +++ b/cmake/FindNumPy.cmake @@ -40,9 +40,17 @@ # Finding NumPy involves calling the Python interpreter if(NumPy_FIND_REQUIRED) + if(GTSAM_PYTHON_VERSION STREQUAL "Default") find_package(PythonInterp REQUIRED) + else() + find_package(PythonInterp ${GTSAM_PYTHON_VERSION} EXACT REQUIRED) + endif() else() + if(GTSAM_PYTHON_VERSION STREQUAL "Default") find_package(PythonInterp) + else() + find_package(PythonInterp ${GTSAM_PYTHON_VERSION} EXACT) + endif() endif() if(NOT PYTHONINTERP_FOUND) diff --git a/cmake/GtsamCythonWrap.cmake b/cmake/GtsamCythonWrap.cmake index 73e2b63e0..6366c1508 100644 --- a/cmake/GtsamCythonWrap.cmake +++ b/cmake/GtsamCythonWrap.cmake @@ -3,8 +3,23 @@ # in the current environment are different from the cached! unset(PYTHON_EXECUTABLE CACHE) unset(CYTHON_EXECUTABLE CACHE) +unset(PYTHON_INCLUDE_DIR CACHE) +unset(PYTHON_MAJOR_VERSION CACHE) + +if(GTSAM_PYTHON_VERSION STREQUAL "Default") + find_package(PythonInterp REQUIRED) + find_package(PythonLibs REQUIRED) +else() + find_package(PythonInterp ${GTSAM_PYTHON_VERSION} EXACT REQUIRED) + find_package(PythonLibs ${GTSAM_PYTHON_VERSION} EXACT REQUIRED) +endif() find_package(Cython 0.25.2 REQUIRED) +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "from __future__ import print_function;import sys;print(sys.version[0], end='')" + OUTPUT_VARIABLE PYTHON_MAJOR_VERSION +) + # User-friendly Cython wrapping and installing function. # Builds a Cython module from the provided interface_header. # For example, for the interface header gtsam.h, @@ -29,12 +44,12 @@ endfunction() function(set_up_required_cython_packages) # Set up building of cython module - find_package(PythonLibs 2.7 REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS}) find_package(NumPy REQUIRED) include_directories(${NUMPY_INCLUDE_DIRS}) endfunction() + # Convert pyx to cpp by executing cython # This is the first step to compile cython from the command line # as described at: http://cython.readthedocs.io/en/latest/src/reference/compilation.html @@ -52,7 +67,7 @@ function(pyx_to_cpp target pyx_file generated_cpp include_dirs) add_custom_command( OUTPUT ${generated_cpp} COMMAND - ${CYTHON_EXECUTABLE} -X boundscheck=False -v --fast-fail --cplus ${includes_for_cython} ${pyx_file} -o ${generated_cpp} + ${CYTHON_EXECUTABLE} -X boundscheck=False -v --fast-fail --cplus -${PYTHON_MAJOR_VERSION} ${includes_for_cython} ${pyx_file} -o ${generated_cpp} VERBATIM) add_custom_target(${target} ALL DEPENDS ${generated_cpp}) endfunction() diff --git a/cython/CMakeLists.txt b/cython/CMakeLists.txt index 8e5d38eec..bbb03440b 100644 --- a/cython/CMakeLists.txt +++ b/cython/CMakeLists.txt @@ -19,7 +19,7 @@ if (GTSAM_INSTALL_CYTHON_TOOLBOX) # wrap gtsam_unstable if(GTSAM_BUILD_UNSTABLE) add_custom_target(gtsam_unstable_header DEPENDS "../gtsam_unstable/gtsam_unstable.h") - set(GTSAM_UNSTABLE_IMPORT "from gtsam_unstable import *") + set(GTSAM_UNSTABLE_IMPORT "from .gtsam_unstable import *") wrap_and_install_library_cython("../gtsam_unstable/gtsam_unstable.h" # interface_header "from gtsam.gtsam cimport *" # extra imports "${GTSAM_CYTHON_INSTALL_PATH}/gtsam_unstable" # install path diff --git a/cython/README.md b/cython/README.md index 368d2a76d..8ba824f8d 100644 --- a/cython/README.md +++ b/cython/README.md @@ -2,20 +2,22 @@ This is the Cython/Python wrapper around the GTSAM C++ library. INSTALL ======= +- if you want to build the gtsam python library for a specific python version (eg 2.7), use the `-DGTSAM_PYTHON_VERSION=2.7` option when running `cmake` otherwise the default interpreter will be used. + - If the interpreter is inside an environment (such as an anaconda environment or virtualenv environment) then the environment should be active while building gtsam. - This wrapper needs Cython(>=0.25.2), backports_abc>=0.5, and numpy. These can be installed as follows: ```bash pip install -r /cython/requirements.txt ``` -- For compatiblity with gtsam's Eigen version, it contains its own cloned version of [Eigency](https://github.com/wouterboomsma/eigency.git), +- For compatibility with gtsam's Eigen version, it contains its own cloned version of [Eigency](https://github.com/wouterboomsma/eigency.git), named **gtsam_eigency**, to interface between C++'s Eigen and Python's numpy. -- Build and install gtsam using cmake with GTSAM_INSTALL_CYTHON_TOOLBOX enabled. -The wrapped module will be installed to GTSAM_CYTHON_INSTALL_PATH, which is -by default: /cython +- Build and install gtsam using cmake with `GTSAM_INSTALL_CYTHON_TOOLBOX` enabled. +The wrapped module will be installed to `GTSAM_CYTHON_INSTALL_PATH`, which is +by default: `/cython` -- Modify your PYTHONPATH to include the GTSAM_CYTHON_INSTALL_PATH: +- Modify your `PYTHONPATH` to include the `GTSAM_CYTHON_INSTALL_PATH`: ```bash export PYTHONPATH=$PYTHONPATH: ``` diff --git a/cython/gtsam/__init__.py.in b/cython/gtsam/__init__.py.in index 7d456023f..85451c680 100644 --- a/cython/gtsam/__init__.py.in +++ b/cython/gtsam/__init__.py.in @@ -1,2 +1,2 @@ -from gtsam import * +from .gtsam import * ${GTSAM_UNSTABLE_IMPORT} diff --git a/wrap/Module.cpp b/wrap/Module.cpp index a3b8df630..a7db9e1f6 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -394,14 +394,18 @@ void Module::emit_cython_pxd(FileWriter& pxdFile) const { /* ************************************************************************* */ void Module::emit_cython_pyx(FileWriter& pyxFile) const { + // directives... + // allow str to automatically coerce to std::string and back (for python3) + pyxFile.oss << "# cython: c_string_type=str, c_string_encoding=ascii\n\n"; + // headers... string pxdHeader = name; pyxFile.oss << "cimport numpy as np\n" "import numpy as npp\n" "cimport " << pxdHeader << "\n" - "from "<< pxdHeader << " cimport shared_ptr\n" - "from "<< pxdHeader << " cimport dynamic_pointer_cast\n" - "from "<< pxdHeader << " cimport make_shared\n"; + "from ."<< pxdHeader << " cimport shared_ptr\n" + "from ."<< pxdHeader << " cimport dynamic_pointer_cast\n" + "from ."<< pxdHeader << " cimport make_shared\n"; pyxFile.oss << "# C helper function that copies all arguments into a positional list.\n" "cdef list process_args(list keywords, tuple args, dict kwargs):\n" diff --git a/wrap/tests/expected-cython/geometry.pyx b/wrap/tests/expected-cython/geometry.pyx index 4bd14b130..cae19d600 100644 --- a/wrap/tests/expected-cython/geometry.pyx +++ b/wrap/tests/expected-cython/geometry.pyx @@ -1,9 +1,11 @@ +# cython: c_string_type=str, c_string_encoding=ascii + cimport numpy as np import numpy as npp cimport geometry -from geometry cimport shared_ptr -from geometry cimport dynamic_pointer_cast -from geometry cimport make_shared +from .geometry cimport shared_ptr +from .geometry cimport dynamic_pointer_cast +from .geometry cimport make_shared # C helper function that copies all arguments into a positional list. cdef list process_args(list keywords, tuple args, dict kwargs): cdef str keyword