4. Создание расширений C и C++

Расширение C для CPython - это разделяемая библиотека (например, файл .so в Linux, .pyd в Windows), которая экспортирует функцию инициализации.

Чтобы быть импортируемой, разделяемая библиотека должна быть доступна на PYTHONPATH, и должна быть названа именем модуля с соответствующим расширением. При использовании distutils правильное имя файла генерируется автоматически.

Функция инициализации имеет сигнатуру:

PyObject *PyInit_modulename(void)

Он возвращает либо полностью инициализированный модуль, либо экземпляр PyModuleDef. Подробности см. в Инициализация модулей C.

Для модулей с именами только ASCII, функция должна быть названа PyInit_<modulename>, при этом <modulename> заменяется именем модуля. При использовании Многофазная инициализация допускаются не ASCII имена модулей. В этом случае имя функции инициализации будет PyInitU_<modulename>, причем <modulename> будет закодировано с использованием кодировки Python punycode с заменой дефисов на символы подчеркивания. В Python:

def initfunc_name(name):
    try:
        suffix = b'_' + name.encode('ascii')
    except UnicodeEncodeError:
        suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
    return b'PyInit' + suffix

Можно экспортировать несколько модулей из одной общей библиотеки, определив несколько функций инициализации. Однако для их импорта требуется использование символических ссылок или пользовательского импортера, поскольку по умолчанию будет найдена только функция, соответствующая имени файла. Подробности см. в разделе «Несколько модулей в одной библиотеке « в PEP 489.

4.1. Создание расширений C и C++ с помощью distutils

Модули расширений могут быть собраны с помощью distutils, который входит в состав Python. Поскольку distutils также поддерживает создание бинарных пакетов, пользователям не обязательно иметь компилятор и distutils для установки расширения.

Пакет distutils содержит сценарий драйвера, setup.py. Это обычный Python-файл, который в самом простом случае может выглядеть следующим образом:

from distutils.core import setup, Extension

module1 = Extension('demo',
                    sources = ['demo.c'])

setup (name = 'PackageName',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])

С этим setup.py, и файлом demo.c, запущенным

python setup.py build

скомпилирует demo.c и создаст модуль расширения с именем demo в каталоге build. В зависимости от системы, файл модуля окажется в подкаталоге build/lib.system и может иметь имя demo.so или demo.pyd.

В setup.py все выполнение происходит путем вызова функции setup. Она принимает переменное количество аргументов в виде ключевых слов, из которых в приведенном примере используется только часть. В частности, в примере указывается метаинформация для создания пакетов, а также указывается содержимое пакета. Обычно пакет содержит дополнительные модули, такие как исходные модули Python, документацию, подпакеты и т.д. Пожалуйста, обратитесь к документации по distutils в Распространение модулей Python (версия Legacy), чтобы узнать больше о возможностях distutils; в этом разделе объясняется только сборка модулей расширения.

Обычно принято предварительно вычислять аргументы для setup(), чтобы лучше структурировать сценарий драйвера. В приведенном выше примере аргумент ext_modules для setup() представляет собой список модулей расширения, каждый из которых является экземпляром Extension. В примере экземпляр определяет расширение с именем demo, которое собирается путем компиляции единственного исходного файла demo.c.

Во многих случаях создание расширения является более сложной задачей, поскольку могут потребоваться дополнительные определения препроцессора и библиотеки. Это показано в приведенном ниже примере.

from distutils.core import setup, Extension

module1 = Extension('demo',
                    define_macros = [('MAJOR_VERSION', '1'),
                                     ('MINOR_VERSION', '0')],
                    include_dirs = ['/usr/local/include'],
                    libraries = ['tcl83'],
                    library_dirs = ['/usr/local/lib'],
                    sources = ['demo.c'])

setup (name = 'PackageName',
       version = '1.0',
       description = 'This is a demo package',
       author = 'Martin v. Loewis',
       author_email = 'martin@v.loewis.de',
       url = 'https://docs.python.org/extending/building',
       long_description = '''
This is really just a demo package.
''',
       ext_modules = [module1])

В этом примере setup() вызывается с дополнительной мета-информацией, что рекомендуется при необходимости сборки дистрибутивных пакетов. Для самого расширения указываются определения препроцессора, каталоги include, каталоги библиотек и библиотеки. В зависимости от компилятора, distutils передает эту информацию компилятору разными способами. Например, на Unix это может привести к командам компиляции

gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o

gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so

Эти строки приведены только для демонстрации; пользователи distutils должны доверять тому, что distutils правильно выполняет вызовы.

4.2. Распространение ваших модулей расширения

Когда расширение успешно построено, существует три способа его использования.

Конечные пользователи обычно хотят установить модуль, они делают это, выполнив

python setup.py install

Сопровождающие модули должны создавать исходные пакеты; для этого они запускают

python setup.py sdist

В некоторых случаях в исходный дистрибутив необходимо включить дополнительные файлы; это делается с помощью файла MANIFEST.in; подробнее см. в разделе Указание файлов для распространения.

Если исходный дистрибутив был успешно собран, сопровождающие также могут создавать бинарные дистрибутивы. В зависимости от платформы, для этого можно использовать одну из следующих команд.

python setup.py bdist_rpm
python setup.py bdist_dumb
Вернуться на верх