我正在尝试使用Cython函数创建一个可安装的Python模块。现在我刚刚实施了 一个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
这应该定义要在Python中使用的两个函数frangi3d
和gradient_filter_3d
。
我知道代码有效,因为我直接在另一个项目中使用它而不安装我自己的包
但是直接从Cython生成的库中导入。
运行 export PATH = / home / user / anaconda3 / bin:$ PATH python setup.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},
)
编辑: 我更新了代码,现在正确导出符号。感谢@phd!
的评论