https://devguide.python.org/development-tools/clinic/
Если вы когда-нибудь смотрели исходники питона, то вы замечали внутри вот такие комментарии (взял за пример `sum()`):
/*[clinic input]
sum as builtin_sum
iterable: object
/
start: object(c_default="NULL") = 0
Return the sum of a 'start' value (default: 0) plus an iterable of numbers.
[clinic start generated code]*/
static PyObject *
builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
/*[clinic end generated code: output=df758cec7d1d302f input=162b50765250d222]*/
{
// ...
}
Есть достаточно понятная проблема: нужно как-то иметь возможность передавать аргументы из Python кода в C код. Учитывая, что бывает много всяких видов Python и C функций (`METH_FASTCALL`,
METH_O и тд), то все становится не так уж и просто.AC позволяет делать достаточно просто описание сигнатуры функции при помощи специального DSL в комментариях.
И даже больше:
- Он генерирует сигнатуру сишной функции со всеми параметрами сразу после тега
[clinic start generated code]- Он хранит последнее состояние в
/*[clinic end generated code: output=df758cec7d1d302f input=162b50765250d222]*/- А еще он создает макросы вида:
PyDoc_STRVAR(builtin_sum__doc__,
"sum($module, iterable, /, start=0)\n"
"--\n"
"\n"
"Return the sum of a \'start\' value (default: 0) plus an iterable of numbers");
#define BUILTIN_SUM_METHODDEF \
{"sum", _PyCFunction_CAST(builtin_sum), METH_FASTCALL|METH_KEYWORDS, builtin_sum__doc__},
Чтобы потом использовать их для добавления методов в модули / классы:
static PyMethodDef builtin_methods[] = {
BUILTIN_SUM_METHODDEF
// ...
};
Как оно внутри?
- Есть большая либа внутри питона для работы с AC (с тестами и mypy)
- Есть make clinic для вызова данной либы на код, который вы меняете
- Можно кастомизировать выполнение либы на питоне, создавая питон код внутри C комментариев
- Мы используем AC даже для C-API тестов
- Сам генератор использует публичный C-API для выдергивания агрументов из переданных объектов. Код генерируется страшный, но читаемый, для примера кусок из файла
Python/clinic/bltinmodule.c.h:
static PyObject *
builtin_sum(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
// ...
static const char * const _keywords[] = {"", "start", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "sum",
.kwtuple = KWTUPLE,
};
PyObject *argsbuf[2];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *iterable;
PyObject *start = NULL;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
if (!args) {
goto exit;
}
iterable = args[0];
if (!noptargs) {
goto skip_optional_pos;
}
start = args[1];
skip_optional_pos:
return_value = builtin_sum_impl(module, iterable, start);
exit:
return return_value;
}
С ним значительно удобнее, чем писать такое руками!
---
Кстати, скоро мы с моими друзьями с Хабра делаем совместную движуху: https://vibe.habr.com/?utm_source=opensource_findings
В программе:
- Общение с разными ребятами, кто занимается карьерой
- Игра в карьерную настолку
- Специальные активности, чтобы понять, какие вайбы в работе подходят именно вам