Создание расширений C и C++
Расширение C для CPython — это общая библиотека (например, файл .so в Linux, .pyd в Windows), которая экспортирует функцию инициализации.
Для импорта общая библиотека должна быть доступна в PYTHONPATH и должна иметь имя, совпадающее с именем модуля, с соответствующим расширением. При использовании distutils правильное имя файла генерируется автоматически.
Функция инициализации имеет следующий вид:
-
PyObject* PyInit_modulename(void)
Она возвращает либо полностью инициализированный модуль, либо экземпляр PyModuleDef. Подробнее см. Инициализация C-модулей.
Для модулей с именами, содержащими только символы ASCII, функция должна именоваться PyInit_<modulename>, где <modulename> заменяется именем модуля. При использовании Многофазной инициализации разрешаются имена модулей, содержащие символы, не являющиеся ASCII. В этом случае имя функции инициализации — PyInitU_<modulename>, где <modulename> закодировано с помощью кодировки punycode Python, при этом дефисы заменяются на нижние подчеркивания. В 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 см. в документации distutils по адресу Распространение Python-модулей (старая версия); в этом разделе описывается только создание модулей расширений.
Часто аргументы для 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; подробнее см. Указание файлов для распространения.
Если пакет исходного кода был создан успешно, авторы также могут создать бинарные дистрибутивы. В зависимости от платформы можно использовать одну из следующих команд.
python setup.py bdist_wininst python setup.py bdist_rpm python setup.py bdist_dumb
© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/extending/building.html