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 в Distributing Python Modules (Legacy version), чтобы узнать больше о функциях 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() с дополнительной метаинформацией, которая рекомендуется при необходимости создания пакетов распространения. Для самого расширения он задает определение препроцессора, включая каталоги, каталоги библиотек и библиотеки. В зависимости от компилятора 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; для получения более подробной информации см. Specifying the files to distribute.

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

python setup.py bdist_wininst
python setup.py bdist_rpm
python setup.py bdist_dumb