Cython + external c++ lib - python

I try run v8 (google javascript engine) in python. Try do this like this example https://developers.google.com/v8/get_started:
setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext
import os
from distutils.sysconfig import get_config_vars
(opt,) = get_config_vars('OPT')
os.environ['OPT'] = " ".join(
flag for flag in opt.split() if flag != '-Wstrict-prototypes'
)
setup(
name = 'helloworld',
ext_modules = cythonize(
Extension(
"helloworld",
["helloworld.pyx"],
language = "c++",
libraries = ["v8"],
library_dirs = ["v8"],
include_dirs = ["v8/include"],
extra_compile_args = ["-std=c++11"],
extra_link_args = ["-std=c++11"],
cmdclass = {'build_ext': build_ext}
)
)
)
helloworld.pyx
from libcpp cimport bool
cdef extern from "v8.h":
cdef bool c_initx "v8::V8::InitializeICU"()
cdef class Script:
def test(self):
c_initx()
return "wow"
Compilation process is OK but i can't import helloworld:
Traceback (most recent call last):
File "test.py", line 1, in <module>
import helloworld
ImportError: /opt/cython/helloworld.so: undefined symbol: _ZN2v82V813InitializeICUEPKc
I use latest v8 version from github https://github.com/v8/v8

Related

How to include shared object files in wheel built from Poetry

I have a simple package that includes a C extension. I'm managing my dependencies and build process with Poetry.
When I run poetry build, the extension is compiled and included in the .tar.gz archive but not in the .whl and I don't understand why. Pip installing from the tar.gz archive works as expected but since the wheel is lacking the .so, pip installing the wheel results in an unusable package.
I've lifted the build machinery from here: https://github.com/python-poetry/poetry/issues/2740
pyproject.toml
[tool.poetry]
name = "python_ctypes"
version = "0.1.0"
description = ""
authors = ["Me"]
include = [
{path = "_extensions/*.so", format = "wheel"}
]
[tool.poetry.dependencies]
python = "^3.9"
numpy = "^1.22.1"
[tool.poetry.dev-dependencies]
[tool.poetry.build]
generate-setup-file = false
script = "build.py"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
build.py
"""Poetry build script for python_ctypes"""
import os
import shutil
from distutils.command.build_ext import build_ext
from distutils.core import Distribution
from distutils.core import Extension
from distutils.errors import CCompilerError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
extensions = [
Extension("python_ctypes._extensions.arraysum", ["python_ctypes/_extensions/arraysum.c"]),
]
class ExtBuilder(build_ext):
# This class allows C extension building to fail.
built_extensions = []
def run(self):
try:
build_ext.run(self)
except (DistutilsPlatformError, FileNotFoundError):
print("Unable to build the C extensions")
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError):
print('Unable to build the "{}" C extension, '
"python_ctypes will use the pure python version of the extension.".format(ext.name))
def build(setup_kwargs):
"""
This function is mandatory in order to build the extensions.
"""
print(setup_kwargs)
distribution = Distribution({"name": "python_ctypes", "ext_modules": extensions})
distribution.package_dir = "python_ctypes"
cmd = ExtBuilder(distribution)
cmd.ensure_finalized()
cmd.run()
# Copy built extensions back to the project
for output in cmd.get_outputs():
relative_extension = os.path.relpath(output, cmd.build_lib)
if not os.path.exists(output):
continue
shutil.copyfile(output, relative_extension)
mode = os.stat(relative_extension).st_mode
mode |= (mode & 0o444) >> 2
os.chmod(relative_extension, mode)
return setup_kwargs
if __name__ == "__main__":
build({})

Building Python submodules from C++ extensions via cmake

I'm trying to incorporate a c++ extension as a submodule into an existing python library via cmake. Building the C++ extension works fine and importing it as a python module works, but not as the submodule of the header library.
I have the following directory structure:
frontend/
foo.py
bar.py
backend/
backend.cpp
The extension is bound to a python module via pybind:
PYBIND11_MODULE(backend, m)
{
m.doc() = "backend c++ implementation"; // optional module docstring
m.def("method", &method, "The method I want to call from python.");
}
In the CMakeLists.txt, the relevant line is:
pybind11_add_module(backend "frontend/backend/backend.cpp")
I've followed the instructions form here and here to write the setup.py script. I guess the most important lines look like this:
from setuptools import setup, Extension, find_packages
from setuptools.command.build_ext import build_ext
from setuptools.command.test import test as TestCommand
class CMakeExtension(Extension):
def __init__(self, name, sourcedir=".", sources=[]):
Extension.__init__(self, name, sources=[])
class CMakeBuild(build_ext):
def run(self):
build_directory = os.path.abspath(self.build_temp)
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
cmake_list_dir = os.path.abspath(os.path.dirname(__file__))
print("-" * 10, "Running CMake prepare", "-" * 40)
subprocess.check_call(
["cmake", cmake_list_dir], cwd=self.build_temp,
)
print("-" * 10, "Building extensions", "-" * 40)
cmake_cmd = ["cmake", "--build", "."] + self.build_args
subprocess.check_call(cmake_cmd, cwd=self.build_temp)
# Move from build temp to final position
for ext in self.extensions:
self.move_output(ext)
def move_output(self, ext):
build_temp = Path(self.build_temp).resolve()
dest_path = Path(self.get_ext_fullpath(ext.name)).resolve()
source_path = build_temp / self.get_ext_filename(ext.name)
dest_directory = dest_path.parents[0]
dest_directory.mkdir(parents=True, exist_ok=True)
self.copy_file(source_path, dest_path)
extensions = [CMakeExtension("backend")]
setup(
name="frontend",
packages=["frontend"],
ext_modules=extensions,
cmdclass=dict(build_ext=CMakeBuild),
)
But this does not make backend a submodule of frontend, but instead a module on its own. So this works:
from backend import method
But to avoid naming issues with other libraries, what I would like to have is this:
from frontend.backend import method
Changing the naming in the pybinding or in the extension call to extensions = [CMakeExtension("frontend.backend")] does unfortunately not resolve my problem, the setup does not find the backend.<platform>.so shared library then, because it looks for frontend/backend.<platform>.so, which does not exist. How could I resolve this issue?
I think I've resolved the issue with the following lines:
Change in the setup.py file:
ext_modules = [
Extension(
"frontend.backend", sources=["frontend/backend/backend.cpp"]
)
]
Change in the CMakeLists.txt file:
pybind11_add_module(backend "frontend/backend/backend.cpp")
set_target_properties( backend
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/frontend"
)
The shared library object backend.platform.so must be located in the frontend directory. Neither the pybind module name nor the sourcefile .cpp should contain any "." in the names, because the get_ext_fullpath() method from build_ext will split by dots. Only the frontend directory containts an init.py file.

undefined symbols in Cython C++ wrapper

I have a working cpp project which builds fine with cmake. Now I need to create a Python wrapper for the same. So I chose cython to bridge the gap between C++ and Python.
Instead of writing the CMakeLists.txt logic in pyx file, I tried compiling the library with cmake itself and later wrap the Cython wrapper around it. Following is the setup.py file:
import os
import sys
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
try:
build_dir = os.path.join(os.getcwd(), "build")
print("building bobcore.a")
if not os.path.exists(build_dir):
os.makedirs(build_dir)
if not os.path.exists(build_dir):
raise Exception("Not able to create `build` directory")
os.chdir(build_dir)
assert os.system("cmake ..") == 0
assert os.system("make -j4") == 0
os.chdir(os.path.abspath(os.path.join(os.getcwd(), os.pardir)))
except:
if not os.path.exists("build/bobcli"):
print("Error building external library, please create libsample.a manually.")
sys.exit(1)
_ext_modules = cythonize([
Extension("call_core",
sources=["call_core.pyx"],
language="c++",
extra_compile_args=["-std=c++11", "-lpthread", "-lz", "-ldl"],
extra_link_args=["-std=c++11"],
include_dirs=[os.path.join(os.getcwd(), "src")], # path to .h file(s)
library_dirs=[os.path.join(os.getcwd(), "build")], # path to .a or .so file(s)
libraries=["bobcli"] # name of pre-built .so file.
)
])
setup(
name='Demos',
ext_modules=_ext_modules,
)
Here is the call_core.pyx file:
cdef extern from "src/api/Api.h" namespace "cli":
cdef cppclass Api:
int getVal()
cdef Api *api = new Api()
def getHelp():
return api.getVal()
del api
Now this file works fine if I implement the getVal() method in the header file itself. But as soon as I move the implementation to .cpp file, Cython compiler shows the following error:
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: /usr/local/lib/python2.7/dist-packages/call_core.so: undefined symbol: _ZN3cli9Api6getValEv
NOTE: The above snippets work perfectly for the functions with their implementation in the .h files.

cimport gives fatal error: 'numpy/arrayobject.h' file not found

I'm trying to teach myself Cython but having problems accessing numpy. The problems seem to be when I use 'cimport'.
For example when importing the following function:
cimport numpy
def cy_sum(x):
cdef numpy.ndarray[int, ndim=1] arr = x
cdef int i, s = 0
for i in range(arr.shape[0]):
s += arr[i]
return s
I get error:
/Users/Daniel/.pyxbld/temp.macosx-10.6-x86_64-2.7/pyrex/test.c:314:10: fatal error: 'numpy/arrayobject.h' file not found
include "numpy/arrayobject.h"
1 error generated.
Any simple instructions on how to resolve this would be much appreciated!
Ok, as has been pointed out above, similar questions have been asked before.
Eg: Make distutils look for numpy header files in the correct place.
I got my code to work by using a setup file with the line
include_dirs = [np.get_include()],
As in:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
ext = [Extension( "cy_test", sources=["cy_test.pyx"] )]
setup(
name = "testing",
cmdclass={'build_ext' : build_ext},
include_dirs = [np.get_include()],
ext_modules=ext
)
I haven't yet tried using pyximport so I'm not sure whether another fix is required for that.

py2exe doesn't import the os module?

So, basically, I'm trying to make my Python 2.7 script into an EXE. Here is my setup.py:
from distutils.core import setup
import py2exe
import os
setup(
options = {
"py2exe":{
"includes": ["os"]
}
},
name = "Anansi CalcPad",
version = "0.35",
description = "Anansi CalcPad is a three-tiered application that provides a journal, calculator, and news aggregator in one.",
author = "Cody Dostal",
author_email = "cody#seafiresoftware.org",
url = "http://seafiresoftware.org/wordpress/anansi-calcpad/",
windows = ["AnansiCalc.py"],
)
If there is indentation problems, it was caused by a bad copy on StackOverflow's part, not in the code.
Here is the error:
Traceback (most recent call last):
File "AnansiCalc.py", line 3, in <module>
ImportError: No module named os
What is wrong?

Categories

Resources