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.
Apliqué una solución para setuptools 288 , cuyo lanzamiento está programado como setuptools 18.0. Esta entrada del registro de cambios describe una técnica que debería funcionar con esa compilación. Una versión beta está disponible para probar.