2012-06-13 8 views
33

Próbuję pobrać niektóre funkcje z dużej biblioteki współdzielonej w C++ (libbig.so) i udostępnić je w języku Python za pośrednictwem Cythona. Aby to zrobić, mam mały plik C++ (small.cpp), który zapewnia cienkie otoki wokół funkcjonalności z udostępnionej biblioteki, której potrzebuję, w sposób, który ułatwia wywoływanie przez Cython (pysmall.pyx).Dystrybucja biblioteki współużytkowanej i trochę kodu C z modułem rozszerzającym Cython

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

mogę zbudować i uruchomić ten moduł rozszerzenia na mój własny komputer: Właśnie kompiluję small.cpp do libsmall.so, a następnie mówię "library = ['small']" w obiekcie Extension w setup.py, aby zbudować moduł rozszerzenia pysmall.so.

Teraz próbuję dystrybuować ten moduł rozszerzeń i mam trudności ze śledzeniem zasobów, które opisują najlepsze praktyki dotyczące dystrybucji, a także bibliotek źródłowych i współużytkowanych w C. Przeczytałem "Installing Python Modules", "Distributing Python Modules" i "Distributing Cython Modules". Rozumiem, jak samodzielnie dystrybuować moduł rozszerzenia. Nie jestem pewien co do najlepszego sposobu na dystrybucję zależności modułu rozszerzenia.

Dokumentacja Cython wskazuje, że należy dołączyć wygenerowane pliki .cpp, a także pliki .pyx, na wypadek, gdyby Cython nie był obecny, ale nie zawiera kodu demonstrującego, jak najlepiej radzić sobie w każdej sytuacji. Nie wspomina się również o tym, jak rozpowszechniać wspólne biblioteki, od których zależy moduł Cython.

Przeszukuję skrypty setup.py z pand, lxml, pyzmq, h5py i innych, i dzieje się sporo poza pracą. Jeśli ktoś ma wskazówki lub przykładowy kod, który może przyspieszyć ten proces, z pewnością to doceniam!

Odpowiedz

16

1) Dystrybucja libbig.so

To jest problem, że pyton nie zamierza pomóc. Do kogo celujesz? Jeśli to jest Linux, czy możesz zażądać zainstalowania go wraz ze swoim menedżerem pakietów? Jeśli libbig nie jest rozprowadzany przez menadżera pakietów lub nie jest linuxem i kierujesz się na wiele architektur, może będziesz musiał rozprowadzić źródło libbig.

2) Cython/setuptools.

Szczerze mówiąc, myślę, że najłatwiej po prostu wymagać, aby ludzie mieli Cythona. W ten sposób istnieje tylko jedna prawidłowa wersja kodu i nie trzeba się martwić niespójnościami między kodem .pyx i .cpp. Najprostszym sposobem na to jest użycie setuptools zamiast distutils. W ten sposób, można użyć:

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

W sumie skrypt setup.py będzie wyglądać następująco:

# 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}) 

Jeśli nie podoba Ci się pomysł wymagający Cython, można zrobić coś takiego:

# 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) 
+2

Należy pamiętać, że w nowszych wersjach 'setuptools' i' distutils' (używam 'setuptools' 5.7), polecenia zostały przeniesione do ich własnych modułów. Więc chciałbyś zrobić 'from setuptools.command.build_ext import build_ext' lub z' distutils' odpowiednio. – Midnighter

+1

Twój pierwszy plik setup.py polega na tym, że importuje plik Cython.Distutils, zanim będzie mógł go zainstalować, jeśli go nie ma. – zneak

+0

Inną opcją byłoby utworzenie pakietu conda, który mógłby być dołączony do pakietu 'libbig.so'. https://conda.io/docs/user-guide/tutorials/build-postgis.html – oLas

7

Oto moje trudne rozwiązanie. Chodzi o to, aby "ukryć" obecność cython, dopóki nie zostanie ona zainstalowana zgodnie z wymaganiami. Można to osiągnąć poprzez leniwą ocenę.Oto przykład:

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 jest fałszywy list, który generuje jego elementy wewnętrzne tylko wtedy, gdy ktoś próbuje uzyskać dostęp do niego.
Gdy jest to wymagane, ta klasa importuje Cython.Build i generuje listę rozszerzeń. Zapobiega to zachowaniu plików *.c w twoim projekcie, co wymaga zainstalowania cythonu podczas budowania modułu.

Bardzo trudne, ale w rzeczywistości działa.

+2

fajny wzór, działa to również z setuptools wymagających cython podczas setup setup z setup_requires :) – marscher

+0

To jest dokładnie to, czego szukałem. Felt lame install-requireing Cython, kiedy Cython został zaimportowany na szczycie skryptu. – csl

5

Napisałem poprawkę dla setuptools 288, która ma zostać wydana jako setuptools 18.0. This changelog entry opisuje technikę, która powinna działać z tą kompilacją. A beta release jest dostępny do testowania.

+0

Co zrobić, jeśli użytkownik ma starszą wersję narzędzia setuptools? –

+0

@RichardHansen Masz kilka opcji. Możesz albo wymagać od użytkowników aktualizacji (prawdopodobnie jest to za wcześnie dzisiaj), pakować ez_setup.py i wymuszać aktualizację setuptools podczas instalacji pakietu (niezalecane) lub wykrywanie wersji setuptools i powrót do mniej eleganckiego podejścia. –

+0

nadal trzeba użyć obejścia z wzorem leniwych list w setuptools> = 18 dla setup_requires deps (! = Cython) @ JasonR.Coombs – marscher

Powiązane problemy