Правильно установить библиотеку Cython как пакет Python - PullRequest
0 голосов
/ 04 июня 2018

Я пытаюсь создать устанавливаемый модуль 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!

...