5 June 2016

Встраивание PyPy кода в приложения на C

PythonProgrammingC
Translation
Tutorial
Original author: Emil Loer
Прим. переводчика:
Как правильно подсказали в комментариях, не смотря на название, речь в данной статье пойдет не о непосредственном встраивании кода, а о создании разделяемых библиотек на Python. Но так как это всего лишь перевод, я все же решил оставить название более близкое к оригиналу.



На конференции PyGrunn 2016 я выступил с докладом о пакете Python cffi и его использовании для встраивания PyPy кода в приложения на C.

С выходом cffi 1.5.0 и его последующим включением в PyPy 5, становится возможным встраивать PyPy код. Это делается путем компиляции кода Python в динамическую библиотеку, которая затем может быть использована в любом другом языке. В этой статье я покажу вам, как это делать.

Встраиваемый API


Первый шаг заключается в определении интерфейса, который определяет, как приложению на С следует вызывать наш Python код. Мы должны указать это с помощью прототипов C-функций. Для нашего примера мы рассмотрим функцию, которая выполняет какие-то вычисления, но, конечно, это может быть все что угодно.
float compute(float first, float second);

Теперь мы должны реализовать эти вычисления на Python:
from my_library import ffi, lib

@ffi.def_extern()
def compute(first, second):
    """ Вычисляет абсолютное расстояние между двумя числами. """
    return abs(first - second)

Этот фрагмент содержит несколько особенных вещей для его правильного встраивания. Первая строка импортирует объекты ffi и lib из динамической библиотеки. Делая это реализация получает доступ к функциям, предоставляемым cffi и их можно использовать для более сложных задач, таких как выделение памяти. Имя my_library определено ниже и соответствует имени нашей динамической библиотеки.

Второе, что мы замечаем в этом фрагменте — это декоратор @ffi.def_extern. Он говорит cffi, что декорированные функции должны быть представлены в публичном API, создаваемой C-библиотеки. Декорированные функции будут сопоставлены с прототипами, указанными в объявлении API и их аргументами и возвращаемые значения будут преобразовываться автоматически.

Скрипт генерирующий библиотеку


Теперь, когда у нас есть API и его реализация, мы должны на самом деле куда-то его встроить. Для этого мы используем скрипт, который генерирует динамическую библиотеку. Он требует, чтобы два фрагмента кода, приведенные выше, находились в файлах api.h и implementation.py.
import cffi
ffi = cffi.FFI()

ffi.embedding_api(open("api.h").read())
ffi.embedding_init_code(open("implementation.py").read())

ffi.set_source("my_library", "")
ffi.compile(verbose=True)

Этот скрипт очень прост. Мы должны указать наш API и предоставить его реализацию. Они оба читаются с диска и соответствуют фрагментам кода, приведенным в предыдущем разделе.

После указания исходников, мы должны сказать cffi название нашей библиотеки. В этом примере это my_library. Кроме того, здесь есть место для добавления дополнительного C-кода, предоставляющего типы для заголовочного файла нашего API, например, с помощью подключения соответствующих заголовочных файлов (что не допускается в embedding_api). Остается только скомпилировать исходники, чтобы создать файл нашей библиотеки.

Запуск скрипта выводит некоторую информацию и создает нашу библиотеку:
$ pypy embed.py
generating ./my_library.c
running build_ext
building 'my_library' extension
...

$ ls my_library.dylib
-rwxr-xr-x  1 djinn  staff  9856 May 15 14:46 my_library.dylib

Все что осталось — это где-нибудь ее использовать!

Использующее приложение


Использовать встраиваемый Python-код на самом деле очень просто. Это можно сделать с помощью следующего кода:
#include <stdio.h>
#include "api.h"

int main(void) {
    float result = compute(12.34f, 10.0f);
    printf("The result: %f\n", result);

    return 0;
}

Как вы видите, для вызова нашего Python-кода практически ничего не нужно. Используя API CPython, вы должны были бы запустить интерпретатор и выполнить множество преобразований параметров и возвращаемого значения. Но не с cffi! Созданная библиотека берет все на себя, так что вы можете сосредоточиться на действительно полезной работе. Последнее, что я должен показать вам, это как скомпилировать и запустить этот код.
$ clang -o test test.c my_library.dylib

$ ./test
The result: 2.340000

И вот оно! С помощью всего нескольких строк кода мы написали программу на C, которая запускает интерпретатор PyPy и выполняет наш Python-код, как если бы это был код на C. Конечно, я показал вам только основы, но это действительно мощная технология. Для получения дополнительной информации, можно посмотреть в документацию cffi.
Tags:cpythonpypycffiembedded python
Hubs: Python Programming C
+18
12.9k 109
Comments 12
Popular right now
Разработчик C++/Python
from 120,000 to 170,000 ₽L3 TechnologiesМосква
Senior C++/Python Developer
from 2,800 to 3,200 $Nitka Technologies, Inc.Remote job
Python Developer
from 80,000 to 200,000 ₽kt.teamRemote job
Python developer
from 170,000 to 200,000 ₽AIR ProductionМосква
Python Разработчик (Python Backend Developer)
from 150,000 ₽Правое полушарие ИнтровертаRemote job
Top of the last 24 hours