tutorial python3 python cython distutils
versión beta

python3 - Distribuir una biblioteca compartida y un código C con un módulo de extensión Cython



cython python3 (3)

1) Distribuir libbig.so

Este es un problema que Python no te va a ayudar. ¿A quién estás apuntando? Si es Linux, ¿puede solicitar que lo instalen con su administrador de paquetes? Si libbig no se distribuye a través de un gestor de paquetes o no es Linux y está apuntando a varias arquitecturas, puede que tenga que distribuir el origen de libbig.

2) Cython / setuptools.

Francamente, creo que es más fácil solo requerir que las personas tengan Cython. De esta forma, solo hay una versión de verdad del código, y no tienes que preocuparte por las incoherencias entre los .pyx y .cpp . La forma más fácil de hacerlo es usar setuptools lugar de distutils . De esa forma, puedes usar:

setup(''mypackage'', ... install_requires=[''cython''])

En total, su script setup.py se verá algo así como:

# setup.py from setuptools import setup, Extension from Cython.Distutils import build_ext pysmall = Extension(''pysmall'', sources = [''pysmall.pyx'', ''small.cpp''], include_dirs = [''include/'']) setup(name=''mypackage'', packages=[''yourpurepythonpackage''], install_requires=[''cython==0.17''], ext_modules=[pysmall], cmdclass = {''build_ext'': build_ext})

Si no te gusta la idea de requerir cython, podrías hacer algo como:

# setup.py import warnings try: from Cython.Distutils import build_ext from setuptools import setup, Extension HAVE_CYTHON = True except ImportError as e: HAVE_CYTHON = False warnings.warn(e.message) from distutils.core import setup, Extension from distutils.command import build_ext pysmall = Extension(''pysmall'', sources = [''pysmall.pyx'', ''small.cpp''], include_dirs = [''include/'']) configuration = {''name'': ''mypackage'', ''packages'': [''yourpurepythonpackage''], ''install_requires'': [''cython==0.17''], ''ext_modules'': [pysmall], ''cmdclass'': {''build_ext'': build_ext}} if not HAVE_CYTHON: pysmall.sources[0] = ''pysmall.cpp'' configuration.pop(''install_requires'') setup(**configuration)

Estoy tratando de tomar algunas funciones de una gran biblioteca compartida de C ++ (libbig.so) y exponerlas a Python a través de Cython. Para hacerlo, tengo un pequeño archivo C ++ (small.cpp) que proporciona una capa delgada alrededor de la funcionalidad de la biblioteca compartida que necesito, de una manera que hace que sea fácil llamar a través de Cython (pysmall.pyx).

libbig.so -> small.cpp, small.h -> libsmall.so -> pysmall.pyx -> pysmall.cpp -> pysmall.so

Puedo construir y ejecutar este módulo de extensión en mi propia computadora: simplemente compilo small.cpp en libsmall.so, y luego digo "libraries = [''small'']" en el objeto Extension en setup.py para construir el módulo de extensión pysmall .asi que.

Ahora estoy tratando de distribuir este módulo de extensión, y me está costando rastrear los recursos que describen las mejores prácticas de setup.py para distribuir un módulo de Cython, así como fuentes C y bibliotecas compartidas. He leído " Instalación de módulos de Python ", " Distribución de módulos de Python " y " Distribución de módulos de Cython ". Entiendo cómo distribuir un módulo de extensión por sí mismo. Estoy menos seguro de la mejor forma de distribuir las dependencias del módulo de extensión.

La documentación de Cython indica que debe incluir los archivos .cpp generados, así como los archivos .pyx, en caso de que Cython no esté presente, pero no proporciona el código para demostrar cómo manejar mejor cada situación. Tampoco menciona cómo distribuir las bibliotecas compartidas de las que depende el módulo Cython.

Estoy explorando los scripts setup.py de pandas, lxml, pyzmq, h5py y más, y hay un poco de trabajo extraño sucediendo. Si alguien tiene punteros o código de ejemplo que podría acelerar este proceso, ciertamente lo agradecería.


Aquí está mi solución engañosa. La idea es "ocultar" la presencia de cython hasta que los requisitos lo instalen. Esto se puede lograr mediante una evaluación perezosa. Aquí hay un ejemplo:

from setuptools import setup, Extension class lazy_cythonize(list): def __init__(self, callback): self._list, self.callback = None, callback def c_list(self): if self._list is None: self._list = self.callback() return self._list def __iter__(self): for e in self.c_list(): yield e def __getitem__(self, ii): return self.c_list()[ii] def __len__(self): return len(self.c_list()) def extensions(): from Cython.Build import cythonize ext = Extension(''native_ext_name'', [''your/src/*.pyx'']) return cythonize([ext]) configuration = { ''name'': ''mypackage'', ''packages'': [''yourpurepythonpackage''], ''install_requires'': [''cython==0.17''], ''ext_modules'': lazy_cythonize(extensions) } setup(**configuration)

lazy_cythonize es una lista falsa que genera sus elementos internos solo cuando alguien intenta acceder a ella.
Cuando es necesario, esta clase importa Cython.Build y genera la lista de extensiones. Esto evita mantener los archivos *.c en su proyecto, lo que requiere que Cython se instale cuando el módulo esté en desarrollo.

Muy complicado, pero en realidad está funcionando.