alpha version

master
Ugo Finnendahl 3 years ago
commit 1db6aa1f6e
  1. 7
      .gitignore
  2. 40
      README.md
  3. 1
      examples/.gitignore
  4. 132
      examples/main.py
  5. 21
      examples/pathtocmake/CMakeLists.txt
  6. 34
      examples/pathtocmake/cmake/FindLIBIGL.cmake
  7. 409
      examples/test.ipynb
  8. 184
      pybindmagic/__init__.py
  9. 35
      setup.py

7
.gitignore vendored

@ -0,0 +1,7 @@
.ipynb_checkpoints
.ccls-cache
build
pbm_modules
*.pyc
__pycache__
pybind11

@ -0,0 +1,40 @@
# pybindmagic
An IPython kernel magic library. After `import pybindmagic` you can use the cell magic `%%cpp`
In this cell you can write cpp code.
## Requirements
* pybind11
* git
* gcc
* cmake (optional)
## TODOS
* [ ] add options for cmake flags
* [ ] add other compilers
* [ ] Better exception handling
* [ ] Better debugging output
## Options
### defs
You need to specify all functions or classes you want to expose to python. Either by `%%cpp -f funcname` or by pybind11 syntax at the end of the cell. There is a shortcut by inserting a `defs` in the end of the cell that will add the correct module name. For an example take a look at the example notebook.
### Compiler
Currently only gcc is supported. The default compiler command is `c++`. You can change the compiler command with:
`pybindmagic.compiler_cmd = "gcc"`
### CMake
With `%%cpp -c path/to/folder/with/CMakeList.txt/in` you can specify cmake compilation. After evaluation pybind11 will be downloaded and moved to this folder, a build folder is generated and the cell is compiled in this folder.
Makesure that you add pybind11 to your cmake file.
### rebuild
With the `%cpp -rebuild` flag you can enforce a complete rebuild.

@ -0,0 +1 @@
libigl

@ -0,0 +1,132 @@
import pybindmagic
import os
os.environ["PATH"] = "/home/ugo/anaconda3/bin" + os.pathsep + os.environ["PATH"]
#%%
%%cpp -f generateMesh
#include <pybind11/eigen.h>
std::tuple<Eigen::MatrixXd,Eigen::MatrixXi> generateMesh()
{
// Inline mesh of a cube
const Eigen::MatrixXd V = (Eigen::MatrixXd(8,3)<<
0.0,0.0,0.0,
0.0,0.0,1.0,
0.0,1.0,0.0,
0.0,1.0,1.0,
1.0,0.0,0.0,
1.0,0.0,1.0,
1.0,1.0,0.0,
1.0,1.0,1.0).finished();
const Eigen::MatrixXi F = (Eigen::MatrixXi(12,3)<<
1,7,5,
1,3,7,
1,4,3,
1,2,4,
3,8,7,
3,4,8,
5,7,8,
5,8,6,
1,5,6,
1,6,2,
2,6,8,
2,8,4).finished().array()-1;
return std::make_tuple(V,F);
}
#%%
V,F = generateMesh()
print("Vertices:\n",V)
print("Faces:\n",F)
#%%
%%cpp
#include <pybind11/eigen.h>
std::tuple<Eigen::MatrixXd,Eigen::MatrixXi> generateMesh()
{
// Inline mesh of a cube
const Eigen::MatrixXd V = (Eigen::MatrixXd(8,3)<<
0.0,0.0,0.0,
0.0,0.0,1.0,
0.0,1.0,0.0,
0.0,1.0,1.0,
1.0,0.0,0.0,
1.0,0.0,1.0,
1.0,1.0,0.0,
1.0,1.0,1.0).finished();
const Eigen::MatrixXi F = (Eigen::MatrixXi(12,3)<<
1,7,5,
1,3,7,
1,4,3,
1,2,4,
3,8,7,
3,4,8,
5,7,8,
5,8,6,
1,5,6,
1,6,2,
2,6,8,
2,8,4).finished().array()-1;
return std::make_tuple(V,F);
}
void printMesh(Eigen::MatrixXd V,Eigen::MatrixXi F)
{
py::print("Vertices:\n", V);
py::print("Faces:\n", F);
}
defs
m.def("generateMesh",&generateMesh);
m.def("printMesh",&printMesh);
#%%
printMesh(*generateMesh())
#%%
%%cpp -c pathtocmake -f viewMesh
#include <igl/opengl/glfw/Viewer.h>
#include <pybind11/eigen.h>
void viewMesh(Eigen::Matrix<double, Eigen::Dynamic, 3> V, Eigen::Matrix<int, Eigen::Dynamic, 3> F)
{
// Plot the mesh
igl::opengl::glfw::Viewer viewer;
viewer.data().set_mesh(V, F);
viewer.data().set_face_based(true);
viewer.launch();
}
#%%
import numpy as np
V = np.array([
[0.0,0.0,0.0],
[0.0,0.0,1.0],
[0.0,1.0,0.0],
[0.0,1.0,1.0],
[1.0,0.0,0.0],
[1.0,0.0,1.0],
[1.0,1.0,0.0],
[1.0,1.0,1.0]])
F = np.array(
[
[1,7,5],
[1,3,7],
[1,4,3],
[1,2,4],
[3,8,7],
[3,4,8],
[5,7,8],
[5,8,6],
[1,5,6],
[1,6,2],
[2,6,8],
[2,8,4]
], dtype=np.int32)-1
viewMesh(V,F)

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.1)
project({name} LANGUAGES CXX)
find_package(Python COMPONENTS Interpreter Development REQUIRED)
add_subdirectory(pybind11)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# libigl
option(LIBIGL_WITH_OPENGL "Use OpenGL" ON)
option(LIBIGL_WITH_OPENGL_GLFW "Use GLFW" ON)
find_package(LIBIGL REQUIRED QUIET)
# Add your project files
file(GLOB SRCFILES *.cpp)
# add_executable(${PROJECT_NAME} ${SRCFILES})
pybind11_add_module(${PROJECT_NAME} ${SRCFILES})
target_link_libraries(${PROJECT_NAME} PRIVATE igl::core igl::opengl_glfw)

@ -0,0 +1,34 @@
# - Try to find the LIBIGL library
# Once done this will define
#
# LIBIGL_FOUND - system has LIBIGL
# LIBIGL_INCLUDE_DIR - **the** LIBIGL include directory
if(LIBIGL_FOUND)
return()
endif()
find_path(LIBIGL_INCLUDE_DIR igl/readOBJ.h
HINTS
${LIBIGL_DIR}
ENV LIBIGL_DIR
PATHS
${CMAKE_SOURCE_DIR}/../..
${CMAKE_SOURCE_DIR}/..
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/libigl
${CMAKE_SOURCE_DIR}/../libigl
${CMAKE_SOURCE_DIR}/../../libigl
/usr
/usr/local
/usr/local/igl/libigl
PATH_SUFFIXES include
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LIBIGL
"\nlibigl not found --- You can download it using:\n\tgit clone https://github.com/libigl/libigl.git ${CMAKE_SOURCE_DIR}/../libigl"
LIBIGL_INCLUDE_DIR)
mark_as_advanced(LIBIGL_INCLUDE_DIR)
list(APPEND CMAKE_MODULE_PATH "${LIBIGL_INCLUDE_DIR}/../cmake")
include(libigl)

@ -0,0 +1,409 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import pybindmagic"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"%%cpp -f generateMesh\n",
"#include <pybind11/eigen.h>\n",
"\n",
"\n",
"std::tuple<Eigen::MatrixXd,Eigen::MatrixXi> generateMesh()\n",
"{\n",
" // Inline mesh of a cube\n",
" const Eigen::MatrixXd V = (Eigen::MatrixXd(8,3)<<\n",
" 0.0,0.0,0.0,\n",
" 0.0,0.0,1.0,\n",
" 0.0,1.0,0.0,\n",
" 0.0,1.0,1.0,\n",
" 1.0,0.0,0.0,\n",
" 1.0,0.0,1.0,\n",
" 1.0,1.0,0.0,\n",
" 1.0,1.0,1.0).finished();\n",
" const Eigen::MatrixXi F = (Eigen::MatrixXi(12,3)<<\n",
" 1,7,5,\n",
" 1,3,7,\n",
" 1,4,3,\n",
" 1,2,4,\n",
" 3,8,7,\n",
" 3,4,8,\n",
" 5,7,8,\n",
" 5,8,6,\n",
" 1,5,6,\n",
" 1,6,2,\n",
" 2,6,8,\n",
" 2,8,4).finished().array()-1;\n",
" return std::make_tuple(V,F);\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vertices:\n",
" [[0. 0. 0.]\n",
" [0. 0. 1.]\n",
" [0. 1. 0.]\n",
" [0. 1. 1.]\n",
" [1. 0. 0.]\n",
" [1. 0. 1.]\n",
" [1. 1. 0.]\n",
" [1. 1. 1.]]\n",
"Faces:\n",
" [[0 6 4]\n",
" [0 2 6]\n",
" [0 3 2]\n",
" [0 1 3]\n",
" [2 7 6]\n",
" [2 3 7]\n",
" [4 6 7]\n",
" [4 7 5]\n",
" [0 4 5]\n",
" [0 5 1]\n",
" [1 5 7]\n",
" [1 7 3]]\n"
]
}
],
"source": [
"V,F = generateMesh()\n",
"print(\"Vertices:\\n\",V)\n",
"print(\"Faces:\\n\",F)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"%%cpp\n",
"#include <pybind11/eigen.h>\n",
"\n",
"\n",
"std::tuple<Eigen::MatrixXd,Eigen::MatrixXi> generateMesh()\n",
"{\n",
" // Inline mesh of a cube\n",
" const Eigen::MatrixXd V = (Eigen::MatrixXd(8,3)<<\n",
" 0.0,0.0,0.0,\n",
" 0.0,0.0,1.0,\n",
" 0.0,1.0,0.0,\n",
" 0.0,1.0,1.0,\n",
" 1.0,0.0,0.0,\n",
" 1.0,0.0,1.0,\n",
" 1.0,1.0,0.0,\n",
" 1.0,1.0,1.0).finished();\n",
" const Eigen::MatrixXi F = (Eigen::MatrixXi(12,3)<<\n",
" 1,7,5,\n",
" 1,3,7,\n",
" 1,4,3,\n",
" 1,2,4,\n",
" 3,8,7,\n",
" 3,4,8,\n",
" 5,7,8,\n",
" 5,8,6,\n",
" 1,5,6,\n",
" 1,6,2,\n",
" 2,6,8,\n",
" 2,8,4).finished().array()-1;\n",
" return std::make_tuple(V,F);\n",
"}\n",
"\n",
"void printMesh(Eigen::MatrixXd V,Eigen::MatrixXi F)\n",
"{\n",
" py::print(\"Vertices:\\n\", V);\n",
" py::print(\"Faces:\\n\", F);\n",
"}\n",
"\n",
"defs\n",
" m.def(\"generateMesh\",&generateMesh);\n",
" m.def(\"printMesh\",&printMesh);"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vertices:\n",
" [[0. 0. 0.]\n",
" [0. 0. 1.]\n",
" [0. 1. 0.]\n",
" [0. 1. 1.]\n",
" [1. 0. 0.]\n",
" [1. 0. 1.]\n",
" [1. 1. 0.]\n",
" [1. 1. 1.]]\n",
"Faces:\n",
" [[0 6 4]\n",
" [0 2 6]\n",
" [0 3 2]\n",
" [0 1 3]\n",
" [2 7 6]\n",
" [2 3 7]\n",
" [4 6 7]\n",
" [4 7 5]\n",
" [0 4 5]\n",
" [0 5 1]\n",
" [1 5 7]\n",
" [1 7 3]]\n"
]
}
],
"source": [
"printMesh(*generateMesh())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# CMAKE example\n",
"\n",
"Take a look at _pathtocmake/CMAKEList.txt_. Its a template cmake file. Make sure that the following lines are present:\n",
"\n",
"```\n",
"project({name} LANGUAGES CXX)\n",
"\n",
"find_package(Python COMPONENTS Interpreter Development REQUIRED)\n",
"add_subdirectory(pybind11)\n",
"pybind11_add_module(${PROJECT_NAME} ${SRCFILES})\n",
"```\n",
"\n",
"This example needs libigl in the pathtocmake folder. Clone it using:\n",
"\n",
"`git clone https://github.com/libigl/libigl.git`"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"cmake:\n",
"-- The CXX compiler identification is GNU 9.3.0\n",
"-- Check for working CXX compiler: /usr/bin/c++\n",
"-- Check for working CXX compiler: /usr/bin/c++ -- works\n",
"-- Detecting CXX compiler ABI info\n",
"-- Detecting CXX compiler ABI info - done\n",
"-- Detecting CXX compile features\n",
"-- Detecting CXX compile features - done\n",
"-- Found Python: /home/ugo/anaconda3/bin/python3.7 (found version \"3.7.6\") found components: Interpreter Development \n",
"-- pybind11 v2.6.3 dev1\n",
"-- Performing Test HAS_FLTO\n",
"-- Performing Test HAS_FLTO - Success\n",
"-- Looking for C++ include pthread.h\n",
"-- Looking for C++ include pthread.h - found\n",
"-- Performing Test CMAKE_HAVE_LIBC_PTHREAD\n",
"-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed\n",
"-- Looking for pthread_create in pthreads\n",
"-- Looking for pthread_create in pthreads - not found\n",
"-- Looking for pthread_create in pthread\n",
"-- Looking for pthread_create in pthread - found\n",
"-- Found Threads: TRUE \n",
"-- Creating target: igl::core (igl)\n",
"-- Creating target: igl::opengl (igl_opengl)\n",
"-- Found OpenGL: /usr/lib/x86_64-linux-gnu/libOpenGL.so found components: OpenGL \n",
"-- The C compiler identification is GNU 9.3.0\n",
"-- Check for working C compiler: /usr/bin/cc\n",
"-- Check for working C compiler: /usr/bin/cc -- works\n",
"-- Detecting C compiler ABI info\n",
"-- Detecting C compiler ABI info - done\n",
"-- Detecting C compile features\n",
"-- Detecting C compile features - done\n",
"-- Creating target: igl::opengl_glfw (igl_opengl_glfw)\n",
"-- Using X11 for window creation\n",
"-- Found X11: /usr/include \n",
"-- Looking for XOpenDisplay in /usr/lib/x86_64-linux-gnu/libX11.so;/usr/lib/x86_64-linux-gnu/libXext.so\n",
"-- Looking for XOpenDisplay in /usr/lib/x86_64-linux-gnu/libX11.so;/usr/lib/x86_64-linux-gnu/libXext.so - found\n",
"-- Looking for gethostbyname\n",
"-- Looking for gethostbyname - found\n",
"-- Looking for connect\n",
"-- Looking for connect - found\n",
"-- Looking for remove\n",
"-- Looking for remove - found\n",
"-- Looking for shmat\n",
"-- Looking for shmat - found\n",
"-- Looking for IceConnectionNumber in ICE\n",
"-- Looking for IceConnectionNumber in ICE - found\n",
"-- Configuring done\n",
"-- Generating done\n",
"-- Build files have been written to: /home/ugo/work/pybindipynb/examples/pathtocmake/build\n",
"\n",
"make:\n",
"Scanning dependencies of target glad\n",
"[ 4%] Building C object glad/CMakeFiles/glad.dir/src/glad.c.o\n",
"[ 9%] Linking C static library libglad.a\n",
"[ 9%] Built target glad\n",
"Scanning dependencies of target glfw\n",
"[ 14%] Building C object glfw/src/CMakeFiles/glfw.dir/context.c.o\n",
"[ 19%] Building C object glfw/src/CMakeFiles/glfw.dir/init.c.o\n",
"[ 23%] Building C object glfw/src/CMakeFiles/glfw.dir/input.c.o\n",
"[ 28%] Building C object glfw/src/CMakeFiles/glfw.dir/monitor.c.o\n",
"[ 33%] Building C object glfw/src/CMakeFiles/glfw.dir/vulkan.c.o\n",
"[ 38%] Building C object glfw/src/CMakeFiles/glfw.dir/window.c.o\n",
"[ 42%] Building C object glfw/src/CMakeFiles/glfw.dir/x11_init.c.o\n",
"[ 47%] Building C object glfw/src/CMakeFiles/glfw.dir/x11_monitor.c.o\n",
"[ 52%] Building C object glfw/src/CMakeFiles/glfw.dir/x11_window.c.o\n",
"[ 57%] Building C object glfw/src/CMakeFiles/glfw.dir/xkb_unicode.c.o\n",
"[ 61%] Building C object glfw/src/CMakeFiles/glfw.dir/posix_time.c.o\n",
"[ 66%] Building C object glfw/src/CMakeFiles/glfw.dir/posix_thread.c.o\n",
"[ 71%] Building C object glfw/src/CMakeFiles/glfw.dir/glx_context.c.o\n",
"[ 76%] Building C object glfw/src/CMakeFiles/glfw.dir/egl_context.c.o\n",
"[ 80%] Building C object glfw/src/CMakeFiles/glfw.dir/osmesa_context.c.o\n",
"[ 85%] Building C object glfw/src/CMakeFiles/glfw.dir/linux_joystick.c.o\n",
"[ 90%] Linking C static library libglfw3.a\n",
"[ 90%] Built target glfw\n",
"Scanning dependencies of target cpp_magic_5329071abe\n",
"[ 95%] Building CXX object CMakeFiles/cpp_magic_5329071abe.dir/cpp_magic_5329071abe.cpp.o\n",
"[100%] Linking CXX shared module cpp_magic_5329071abe.cpython-37m-x86_64-linux-gnu.so\n",
"[100%] Built target cpp_magic_5329071abe\n",
"\n"
]
}
],
"source": [
"%%cpp -c pathtocmake -f viewMesh -rebuild\n",
"\n",
"#include <igl/opengl/glfw/Viewer.h>\n",
"#include <pybind11/eigen.h>\n",
"\n",
"\n",
"void viewMesh(Eigen::Matrix<double, Eigen::Dynamic, 3> V, Eigen::Matrix<int, Eigen::Dynamic, 3> F)\n",
"{\n",
" // Plot the mesh\n",
" igl::opengl::glfw::Viewer viewer;\n",
" viewer.data().set_mesh(V, F);\n",
" viewer.data().set_face_based(true);\n",
" viewer.launch();\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"ename": "ImportError",
"evalue": "dynamic module does not define module export function (PyInit_cpp_magic_f96b7fce74)",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-5-d51271ddbf06>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mcpp_magic_f96b7fce74\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;31m#viewMesh(V,F)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mImportError\u001b[0m: dynamic module does not define module export function (PyInit_cpp_magic_f96b7fce74)"
]
}
],
"source": [
"import cpp_magic_f96b7fce74\n",
"#viewMesh(V,F)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"V = np.array([\n",
" [0.0,0.0,0.0],\n",
" [0.0,0.0,1.0],\n",
" [0.0,1.0,0.0],\n",
" [0.0,1.0,1.0],\n",
" [1.0,0.0,0.0],\n",
" [1.0,0.0,1.0],\n",
" [1.0,1.0,0.0],\n",
" [1.0,1.0,1.0]])\n",
"\n",
"F = np.array(\n",
" [\n",
" [1,7,5],\n",
" [1,3,7],\n",
" [1,4,3],\n",
" [1,2,4],\n",
" [3,8,7],\n",
" [3,4,8],\n",
" [5,7,8],\n",
" [5,8,6],\n",
" [1,5,6],\n",
" [1,6,2],\n",
" [2,6,8],\n",
" [2,8,4]\n",
" ], dtype=np.int32)-1\n",
"viewMesh(V,F)"
]
}
],
"metadata": {
"@webio": {
"lastCommId": null,
"lastKernelId": null
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
}
},
"nbformat": 4,
"nbformat_minor": 2
}

@ -0,0 +1,184 @@
from IPython.core.magic import register_cell_magic, needs_local_scope
import importlib
import glob
import sys, os
import random
import string
import hashlib
import shutil
import subprocess
compiler_cmd = "c++"
cmd_flags = ["$(pkg-config --cflags eigen3)"]
template = """
#include <pybind11/pybind11.h>
namespace py = pybind11;
{cell_code}
PYBIND11_MODULE({name}, m)
{defs}
"""
def_template = '\nm.def("{function}", &{function});\n'
tmp_path = "pbm_modules"
# Make sure the correct python executeable is used using cmake
os.environ["PATH"] = os.path.dirname(sys.executable) + os.pathsep + os.environ["PATH"]
def exec_command(cmd, cwd=None,**kwargs):
try:
output = subprocess.check_output(cmd,cwd=cwd,stderr=subprocess.STDOUT,**kwargs)
if isinstance(output,bytes):
return False, output.decode("utf-8")
return False, output
except subprocess.CalledProcessError as e:
if isinstance(e.output,bytes):
return True, e.output.decode("utf-8")
return True, e.output
def ensure_tmp_folder():
path = os.path.join(os.getcwd(), tmp_path)
if not os.path.exists(path):
os.makedirs(path)
if not path in sys.path:
sys.path.append(path)
def ensure_pybind(path, force=False):
path = os.path.join(path,"pybind11")
exists = os.path.exists(path)
if exists and force:
shutil.rmtree(os.path.join(path,"pybind11"))
if not exists:
err, output = exec_command(["git", "clone","https://github.com/pybind/pybind11.git",path])
def ensure_build_dir(path, clear=False):
path = os.path.join(path, "build")
exists = os.path.exists(path)
if exists and clear:
shutil.rmtree(path)
if not exists or clear:
os.makedirs(path)
return exists and not clear
@register_cell_magic
def cpp(line, cell, *what):
"""Compile, execute C++ code wrapped with pybind11 and import it"""
args = line.split(" ")
rebuild = False
cmake_path = None
function = ""
run_cmake = False
# TODO: Add full argument parser
for i,arg in enumerate(args):
if arg == "-c":
cmake_path = args[i+1].strip()
if arg == "-f":
function = args[i+1].strip()
if arg == "-rebuild":
rebuild = True
# We first retrieve the current IPython interpreter
# instance.
ip = get_ipython()
cmake = ""
if cmake_path is not None:
with open(os.path.join(cmake_path,"CMakeLists.txt"), 'r') as f:
cmake = f.read()
# name = ''.join(random.choices(string.ascii_lowercase, k=10))
hash_object = hashlib.sha256((cmake+" ".join(line)+function+cell).encode())
name = "cpp_magic_"+hash_object.hexdigest()[:10]
if cmake_path is None:
ensure_tmp_folder()
else:
run_cmake = not ensure_build_dir(cmake_path, clear=rebuild)
p = os.path.abspath(os.path.join(cmake_path,"build"))
if p not in sys.path:
sys.path.append(p)
ensure_pybind(cmake_path)
if cmake_path is not None:
path = cmake_path
else:
path = tmp_path
# We define the source and executable filenames.
try:
try:
if rebuild:
raise Exception()
mdl = importlib.import_module(name)
except:
exe = sys.executable
split = cell.split("defs")
if len(split) == 1 and function == "":
raise Exception("You have to name a function as argument ('%%cpp -f <functionname>') or manual set defs")
split.append("")
curr_defs = split[1]
if function != "":
curr_defs += def_template.format(function=function)
# We write the code to the C++ file.
with open(os.path.join(path,f"{name}.cpp"), 'w') as f:
f.write(template.format(cell_code=split[0],name=name,defs="{"+curr_defs+"}"))
if cmake_path is None:
# We compile the C++ code into an executable.
command = f"{compiler_cmd} {' '.join(cmd_flags)} -O3 -Wall -shared -std=c++11 -fPIC $({exe} -m pybind11 --includes) {tmp_path}/{name}.cpp -o {tmp_path}/{name}.so"
compile = ip.getoutput(command)
if len(compile) != 0:
raise Exception("\n".join(compile))
else:
with open(os.path.join(cmake_path,"CMakeLists.txt"), 'w') as f:
f.write(cmake.replace("{name}",name))
# TODO: Add option to add cmake flags
if run_cmake:
command = ["cmake", ".."]
print("cmake:")
err, output = exec_command(command, cwd=os.path.join(cmake_path, "build"))
if err:
raise Exception(output)
print(output)
command = ["make"]
print("make:")
err, output = exec_command(command, cwd=os.path.join(cmake_path, "build"))
if err:
raise Exception(output)
print(output)
with open(os.path.join(cmake_path,"CMakeLists.txt"), 'w') as f:
f.write(cmake)
# We execute the executable and return the output.
# get a handle on the module
mdl = importlib.import_module(name)
if os.path.exists(os.path.join(path,f"{name}.cpp")):
os.remove(os.path.join(path,f"{name}.cpp"))
# is there an __all__? if so respect it
if "__all__" in mdl.__dict__:
names = mdl.__dict__["__all__"]
else:
# otherwise we import all names that don't begin with _
names = [x for x in mdl.__dict__ if not x.startswith("_")]
except Exception as e:
with open(os.path.join(cmake_path,"CMakeLists.txt"), 'w') as f:
f.write(cmake)
if os.path.exists(os.path.join(path,f"{name}.cpp")):
os.remove(os.path.join(path,f"{name}.cpp"))
raise e
# now drag them in
ip.push({k: getattr(mdl, k) for k in names})

@ -0,0 +1,35 @@
from setuptools import setup, find_packages
import os
# Utility function to read the README file.
# Used for the long_description. It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name="pybindmagic",
version="0.1",
packages=find_packages(),
# requires=['pybind11'],
# scripts=['say_hello.py'],
# install_requires=['python_version>=3.5'],
# metadata to display on PyPI
author="Ugo Finnendahl",
author_email="finnendahl@tu-berlin.de",
description="This is a simple pybind11 wrapper as ipython cellmagic.",
keywords="ipython, jupyter, cpp, c++, cell_magic",
# url="http://example.com/HelloWorld/", # project home page, if any
# project_urls={
# "Bug Tracker": "https://bugs.example.com/HelloWorld/",
# "Documentation": "https://docs.example.com/HelloWorld/",
# "Source Code": "https://code.example.com/HelloWorld/",
# },
# classifiers=[
# 'License :: OSI Approved :: Python Software Foundation License'
# ],
python_requires=">=3.5",
long_description=read('README.md')
)
Loading…
Cancel
Save