2012-06-13 26 views
33

Tôi đang cố gắng thực hiện một số chức năng từ thư viện chia sẻ C++ lớn (libbig.so) và hiển thị chúng bằng Python qua Cython. Để làm như vậy, tôi đã có một tệp C++ nhỏ (small.cpp) cung cấp một trình bao bọc mỏng xung quanh chức năng từ thư viện được chia sẻ mà tôi cần, theo cách giúp dễ dàng gọi qua Cython (pysmall.pyx).Phân phối thư viện được chia sẻ và một số mã C với mô-đun mở rộng Cython

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

tôi có thể xây dựng và chạy mô-đun tiện ích này trên của tôi máy tính của riêng tôi: Tôi chỉ biên dịch small.cpp thành libsmall.so và sau đó nói "libraries = ['small']" trong đối tượng Extension trong setup.py để xây dựng mô-đun mở rộng pysmall.so.

Tôi đang cố gắng phân phối mô-đun mở rộng này và tôi đang gặp khó khăn trong việc theo dõi tài nguyên mô tả các phương pháp hay nhất về phân phối mô-đun Cython cũng như nguồn C và thư viện được chia sẻ. Tôi đã đọc qua "Installing Python Modules", "Distributing Python Modules" và "Distributing Cython Modules". Tôi hiểu cách tự phân phối mô-đun mở rộng. Tôi ít chắc chắn về cách tốt nhất để phân phối phụ thuộc của mô-đun mở rộng.

Tài liệu Cython cho biết bạn nên bao gồm tệp .cpp được tạo cũng như tệp .pyx, trong trường hợp Cython không có, nhưng nó không cung cấp mã để minh họa cách xử lý tốt nhất từng tình huống. Nó cũng không đề cập đến cách phân phối các thư viện chia sẻ mà trên đó mô-đun Cython phụ thuộc.

Tôi đang tìm hiểu thông qua các tập lệnh setup.py từ gấu trúc, lxml, pyzmq, h5py, v.v. và có khá nhiều công việc không liên quan xảy ra. Nếu bất cứ ai có con trỏ hoặc mã ví dụ có thể đẩy nhanh quá trình này, tôi chắc chắn sẽ đánh giá cao nó!

Trả lời

16

1) Phân phối libbig.so

Đây là vấn đề mà python sẽ không giúp bạn. Bạn đang nhắm mục tiêu ai? Nếu đó là Linux, bạn có thể yêu cầu họ cài đặt nó bằng trình quản lý gói của họ không? Nếu libbig không được phân phối thông qua trình quản lý gói hoặc nó không phải là linux và bạn đang nhắm mục tiêu nhiều kiến ​​trúc, bạn có thể phải phân phối nguồn libbig.

2) Cython/setuptools.

Thành thật mà nói, tôi nghĩ đơn giản nhất là chỉ yêu cầu mọi người có Cython. Bằng cách này, chỉ có một phiên bản chân lý cơ bản của mã và bạn không phải lo lắng về những mâu thuẫn giữa mã số .pyx.cpp. Cách dễ nhất để thực hiện việc này là sử dụng setuptools thay vì distutils. Bằng cách đó, bạn có thể sử dụng:

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

Tổng cộng, setup.py kịch bản của bạn sẽ giống như thế:

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

Nếu bạn không thích ý tưởng đòi hỏi cython, bạn có thể làm một cái gì đó như:

# 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

Xin lưu ý rằng trong các phiên bản 'setuptools' và' distutils' mới hơn (tôi đang sử dụng 'setuptools' 5.7), các lệnh đã được chuyển sang mô-đun riêng của chúng. Vì vậy, bạn sẽ muốn làm 'từ setuptools.command.build_ext nhập build_ext' hoặc từ' distutils' tương ứng. – Midnighter

+1

setup.py đầu tiên của bạn là nó nhập khẩu Cython.Distutils trước khi nó có cơ hội để cài đặt nó nếu nó không có mặt. – zneak

+0

Một tùy chọn khác là tạo một gói conda, có thể được đóng gói 'libbig.so'. https://conda.io/docs/user-guide/tutorials/build-postgis.html – oLas

7

Đây là giải pháp khôn lanh của tôi. Ý tưởng là "ẩn" sự hiện diện của cython cho đến khi nó được cài đặt theo yêu cầu. Điều này có thể đạt được bằng cách đánh giá lười biếng.Dưới đây là ví dụ:

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 là danh sách giả tạo thành phần bên trong khi ai đó cố gắng truy cập vào phần tử đó.
Khi được yêu cầu, lớp này nhập Cython.Build và tạo danh sách các tiện ích mở rộng. Điều này tránh để giữ các tệp *.c trong dự án của bạn, yêu cầu phải cài đặt cython khi mô-đun đang xây dựng.

Khá phức tạp, nhưng thực sự nó hoạt động.

+2

mô hình thú vị, thao tác này cũng hoạt động với các công cụ thiết lập yêu cầu cython trong suốt thời gian chạy thiết lập với setup_requires :) – marscher

+0

Đây chính xác là những gì tôi đang tìm kiếm. Cảm thấy yêu cầu Cython cài đặt yêu cầu, khi Cython được nhập ở đầu tập lệnh. – csl

5

Tôi đã đẩy bản sửa lỗi cho setuptools 288, dự kiến ​​phát hành dưới dạng công cụ thiết lập 18.0. This changelog entry mô tả một kỹ thuật cần làm việc với bản dựng đó. A beta release có sẵn để kiểm tra.

+0

Điều gì xảy ra nếu người dùng có phiên bản thiết lập cũ hơn? –

+0

@RichardHansen Bạn có một vài lựa chọn. Bạn có thể yêu cầu người dùng nâng cấp (có thể quá sớm ngày hôm nay), bó ez_setup.py và buộc nâng cấp setuptools trong khi cài đặt gói của bạn (không được khuyến nghị) hoặc phát hiện phiên bản setuptools và dự phòng theo cách tiếp cận ít thanh lịch hơn. –

+0

vẫn phải sử dụng giải pháp thay thế với mẫu danh sách lười trong tập tin thiết lập> = 18 cho các tập tin setup_requires deps (! = Cython) @ JasonR.Coombs – marscher

Các vấn đề liên quan