python - Construyendo una biblioteca de Ctypes- "basada" con distutils
(2)
Algunas aclaraciones aquí:
No es una biblioteca "basada en ctypes". Es solo una biblioteca de C estándar, y desea instalarla con distutils. Si utiliza una extensión C, ctypes o cython para envolver esa biblioteca es irrelevante para la pregunta.
Como la biblioteca aparentemente no es genérica, pero solo contiene optimizaciones para su aplicación, la recomendación a la que se vincula no se aplica a usted, en su caso es probablemente más fácil escribir una extensión C o usar Cython, en cuyo caso se evita su problema
Para la pregunta real, siempre puede usar su propio comando distutils personalizado y, de hecho, una de las discusiones vinculadas a tal comando, el comando OOF2 build_shlib
, que hace lo que quiere. En este caso, aunque desea instalar una biblioteca personalizada que realmente no se comparte, y luego creo que no necesita instalarla en / usr / lib / yourproject, pero puede instalarla en el directorio del paquete en / usr / lib / python-xx / site-packages / yourmodule, junto con sus archivos de python. Pero no estoy 100% seguro de eso, así que tendrás que intentarlo.
Siguiendo esta recomendación , he escrito una biblioteca de extensión C nativa para optimizar parte de un módulo de Python a través de ctypes. Elegí ctypes sobre escribir una biblioteca nativa de CPython porque era más rápida y fácil (solo algunas funciones con todos los bucles ajustados dentro).
Ahora he golpeado un obstáculo. Si quiero que mi trabajo se pueda instalar fácilmente usando los nombres de usuario que utilizan la instalación de python setup.py install
, entonces debe ser capaz de construir mi biblioteca compartida e instalarla (probablemente en /usr/lib/ myproject
). Sin embargo, esto no es un módulo de extensión de Python, y por lo que puedo decir, distutils no puede hacer esto.
He encontrado algunas referencias a otras personas con este problema:
- Alguien en una gran discusión con un hackeo en 2006 .
- Alguien preguntando por distutils-sig y no obteniendo respuesta .
- Alguien que pregunte en la lista de python principal y que se apunte a las entrañas de un proyecto existente .
Soy consciente de que puedo hacer algo nativo y no usar los nombres de la biblioteca compartida, o incluso usar el sistema de empaquetado de mi distribución. Mi preocupación es que esto limitará la facilidad de uso ya que no todos podrán instalarlo fácilmente.
Entonces, mi pregunta es: ¿cuál es la mejor forma actual de distribuir una biblioteca compartida con los nombres que serán utilizados por ctypes pero que, de lo contrario, es nativo del sistema operativo y no un módulo de extensión de Python?
Siéntase libre de responder con uno de los hacks vinculados arriba si puede expandirse y justificar por qué esa es la mejor manera. Si no hay nada mejor, al menos toda la información estará en un solo lugar.
La documentación de distutils here establece que:
La extensión AC para CPython es una biblioteca compartida (por ejemplo, un archivo .so en Linux, .pyd en Windows), que exporta una función de inicialización.
Así que la única diferencia con respecto a una biblioteca compartida simple parece ser la función de inicialización (además de una convención de nomenclatura de archivos sensata con la que no creo que tenga ningún problema). Ahora, si echa un vistazo a distutils.command.build_ext
verá que define un método get_export_symbols()
que:
Devuelve la lista de símbolos que una extensión compartida debe exportar. Esto usa ''ext.export_symbols'' o, si no se proporciona, "PyInit_" + nombre_módulo. Solo relevante en Windows, donde el archivo .pyd (DLL) debe exportar la función del módulo "PyInit_".
Por lo tanto, usarlo para bibliotecas compartidas simples debería funcionar de manera inmediata, excepto en Windows. Pero también es fácil arreglar eso. El valor de retorno de get_export_symbols()
se pasa a distutils.ccompiler.CCompiler.link()
, que indica la documentación:
''export_symbols'' es una lista de símbolos que la biblioteca compartida exportará. (Esto parece ser relevante sólo en Windows.)
Así que no agregar la función de inicialización a los símbolos de exportación hará el truco. Para eso solo necesitas invalidar trivialmente build_ext.get_export_symbols()
.
Además, es posible que desee simplificar el nombre del módulo. Este es un ejemplo completo de una subclase build_ext
que puede construir módulos de ctypes así como módulos de extensión:
from distutils.core import setup, Extension
from distutils.command.build_ext import build_ext
class build_ext(build_ext):
def build_extension(self, ext):
self._ctypes = isinstance(ext, CTypes)
return super().build_extension(ext)
def get_export_symbols(self, ext):
if self._ctypes:
return ext.export_symbols
return super().get_export_symbols(ext)
def get_ext_filename(self, ext_name):
if self._ctypes:
return ext_name + ''.so''
return super().get_ext_filename(ext_name)
class CTypes(Extension): pass
setup(name=''testct'', version=''1.0'',
ext_modules=[CTypes(''ct'', sources=[''testct/ct.c'']),
Extension(''ext'', sources=[''testct/ext.c''])],
cmdclass={''build_ext'': build_ext})