正确安装Cython库作为Python包

时间:2018-06-04 14:53:12

标签: python cython distutils

我正在尝试使用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中使用的两个函数frangi3dgradient_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!

的评论

0 个答案:

没有答案