Я пытаюсь создать устанавливаемый модуль Python, используя функции Cython.На данный момент я только что реализовал одну функцию 3d-фильтра и хочу, чтобы ее можно было установить, прежде чем выполнять больше работы.
В моей структуре папок существуют следующие файлы:
./
├ mymodule/
| ├ frangi_filter/
| | ├ include/
| | | └ .. some more header files
| | ├ source/
| | | └ .. some more *.cpp files
| | ├ __init__.py
| | ├ Frangi3D_c.pxd
| | └ Frangi3D_c.pyx
| ├ include/
| | └ .. some common header files
| ├ source/
| | └ .. some common *.cpp files
| └ __init__.py
└ setup.py
Это должно определить двафункции frangi3d
и gradient_filter_3d
для использования в Python.Я знаю, что код работает, потому что я использую его в другом проекте напрямую, не устанавливая свой собственный пакет, а напрямую импортируя из сгенерированной библиотеки Cython.
Запуск экспорта PATH = / home / user / anaconda3 / bin: $ PATH pythonsetup.py install --user
создает пакет в установленной папке, но попытка увидеть доступные функции в пакете приводит к
In[1]: import mymodule
Traceback (most recent call last):
File "/home/vlab/anaconda3/lib/python3.6/site-packages/IPython/cor e/interactiveshell.py", line 2862, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-62050632ee79>", line 1, in <module>
import mymodule
File "/usr/local/bin/pycharm/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
module = self._system_import(name, *args, **kwargs)
File "/home/user/.local/lib/python3.6/site-packages/mymodule/__init__.py", line 10, in <module>
from .frangi_filter import frangi3d, gradient_filter_3d
File "/usr/local/bin/pycharm/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
module = self._system_import(name, *args, **kwargs)
File "/home/user/.local/lib/python3.6/site-packages/mymodule/frangi_filter/__init__.py", line 10, in <module>
from .Frangi3D_c import *
File "/usr/local/bin/pycharm/helpers/pydev/_pydev_bundle/pydev_import_hook.py", line 21, in do_import
module = self._system_import(name, *args, **kwargs)
ImportError: /home/user/.local/lib/python3.6/site-packages/mymodule/frangi_filter/Frangi3D_c.cpython-36m-x86_64-linux-gnu.so: undefined symbol: _Z14FrangiFilter3DPfjjjfffS_jS_
Таким образом, функции как-то скомпилированы, но не находятся вбиблиотека *.so
.Разбивка имени с помощью nm -C *.so --undefined
показывает:
U __vsnprintf_chk@@GLIBC_2.3.4
U FrangiFilter3D(float*, unsigned int, unsigned int, unsigned int, float, float, float, float*, unsigned int, float*, bool)
U GradientFilter3(float const*, int, int, int, unsigned char, float*)
U std::ios_base::Init::Init()@@GLIBCXX_3.4
U std::ios_base::Init::~Init()@@GLIBCXX_3.4
и некоторые другие неопределенные символы, по-видимому, связанные с объектами Python.Как уже упоминалось, сам код должен работать, потому что я использую его в другом модуле, но не устанавливая его как пакет.
Мой файл setup.py выглядит следующим образом (и взят из [https://github.com/cython/cython/wiki/PackageHierarchy]):
import sys, os
from distutils.core import setup
from distutils.extension import Extension
# we'd better have Cython installed, or it's a no-go
try:
from Cython.Distutils import build_ext
except:
print("You don't seem to have Cython installed. Please get a")
print("copy from www.cython.org and install it")
sys.exit(1)
def scandir(d, files=[]):
"""scan the directory for extension files, converting
them to extension names in dotted notation"""
home_dir = os.path.dirname(os.path.relpath(__file__)) # setup.py dir
for filename in os.listdir(d):
path = os.path.join(home_dir, d, filename)
if os.path.isfile(path):
if path.endswith(".pyx"):
to_append = path.replace(os.path.sep, ".")[:-4]
print(f"converting to: {to_append}")
files.append(to_append)
elif os.path.isdir(path):
scandir(path, files)
return files
# generate an Extension object from its dotted name
def makeExtension(extName):
extPath = extName.replace(".", os.path.sep)+".pyx"
[ext_base, ext_filename] = os.path.split(extPath)
return Extension(
extName,
[extPath],
include_dirs = [".", ext_base, ext_base+"/include"],
extra_compile_args = ["-O3", "-Wall"],
extra_link_args = ['-g'],
language="C++",
)
# get the list of extensions
extNames = scandir("ChIMP3D")
# and build up the set of Extension objects
extensions = []
for name in extNames:
print(f"making extension for: {name}")
new_extension = makeExtension(name)
extensions.append(new_extension)
# finally, we can pass all this to distutils
setup(
name="mymodule",
packages=["mymodule", "mymodule.frangi_filter"],
package_data ={
"package": ["*.py", "*.pyc",
"frangi_filter/*.py", "frangi_filter/*.so"]
},
ext_modules=extensions,
cmdclass = {'build_ext': build_ext},
)
edit: я обновил код и теперь символы экспортируются правильно. Спасибо за комментарий от @phd!