clinic.md
1> **Источник:** https://python-all.ru/3.4/howto/clinic.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Руководство по Argument Clinic89| автор: | Larry Hastings |10| --- | --- |1112Аннотация1314Argument Clinic – это препроцессор для C-файлов CPython. Его цель – автоматизировать всю шаблонную работу, связанную с написанием кода разбора аргументов для «встроенных функций». Этот документ показывает, как преобразовать первую C-функцию для работы с Argument Clinic, а затем знакомит с некоторыми продвинутыми темами по использованию Argument Clinic.1516В настоящее время Argument Clinic считается внутренним инструментом только для CPython. Его использование не поддерживается для файлов за пределами CPython, и не даётся никаких гарантий обратной совместимости в будущих версиях. Другими словами: если вы поддерживаете внешнее C-расширение для CPython, вы можете экспериментировать с Argument Clinic в своём коде. Но версия Argument Clinic, поставляемая с CPython 3.5, *может* оказаться полностью несовместимой и сломать весь ваш код.1718## Цели Argument Clinic1920Основная цель Argument Clinic – взять на себя всю работу по разбору аргументов внутри CPython. Это означает, что при переводе функции на работу с Argument Clinic эта функция больше не должна выполнять собственный разбор аргументов: сгенерированный код должен быть «чёрным ящиком», в который CPython передаёт управление сверху, а вызываемый код находится снизу, при этом `PyObject *args` (и, возможно, `PyObject *kwargs`) магическим образом преобразуются в необходимые переменные и типы C.2122Чтобы Argument Clinic достиг своей основной цели, он должен быть прост в использовании. Сейчас работа с библиотекой разбора аргументов CPython – это рутина, требующая поддержания избыточной информации в удивительно большом количестве мест. При использовании Argument Clinic вам не нужно повторяться.2324Очевидно, никто не захочет использовать Argument Clinic, если он не решает их проблемы – и не создаёт при этом новых. Поэтому крайне важно, чтобы Argument Clinic генерировал корректный код. Хорошо бы, чтобы код был ещё и быстрее, но, по крайней мере, он не должен приводить к существенной регрессии производительности. (В конечном счёте Argument Clinic *должен* сделать возможным значительное ускорение – мы могли бы переписать его генератор кода, чтобы он создавал специализированный код разбора аргументов вместо вызова универсальной библиотеки разбора аргументов CPython. Это позволило бы добиться максимально быстрого разбора аргументов!)2526Кроме того, Argument Clinic должен быть достаточно гибким, чтобы работать с любым подходом к разбору аргументов. В Python есть функции с очень странным поведением при разборе; цель Argument Clinic – поддерживать все из них.2728Наконец, изначальной мотивацией для Argument Clinic было предоставление «сигнатур» для интроспекции встроенных функций CPython. Раньше функции запросов интроспекции выбрасывали исключение, если передать встроенную функцию. С Argument Clinic это осталось в прошлом!2930Одна идея, которую стоит помнить при работе с Argument Clinic: чем больше информации вы ему дадите, тем лучше он сможет выполнить свою работу. Argument Clinic, безусловно, сейчас относительно прост. Но по мере развития он будет становиться всё более сложным и сможет делать много интересных и умных вещей со всей предоставленной информацией.3132## Основные концепции и использование3334Argument Clinic поставляется вместе с CPython; его можно найти в `Tools/clinic/clinic.py`. Если запустить этот скрипт, указав в качестве аргумента C-файл:3536```python37% python3 Tools/clinic/clinic.py foo.c38```3940Argument Clinic будет сканировать файл в поисках строк, которые выглядят точно так же:4142```python43/*[clinic input]44```4546Когда он находит такую строку, он читает всё до строки, которая выглядит точно так же:4748```python49[clinic start generated code]*/50```5152Всё, что находится между этими двумя строками, является входными данными для Argument Clinic. Все эти строки, включая начальную и конечную строки комментариев, вместе называются «блоком» Argument Clinic.5354Когда Argument Clinic анализирует один из таких блоков, он генерирует выходные данные. Эти выходные данные перезаписываются в C-файл сразу после блока, за которыми следует комментарий с контрольной суммой. Блок Argument Clinic теперь выглядит так:5556```python57/*[clinic input]58... clinic input goes here ...59[clinic start generated code]*/60... clinic output goes here ...61/*[clinic end generated code: checksum=...]*/62```6364Если запустить Argument Clinic на том же файле повторно, он отбросит старые выходные данные и запишет новые с новой строкой контрольной суммы. Однако если входные данные не изменились, выходные данные тоже не изменятся.6566Никогда не следует изменять выходную часть блока Argument Clinic. Вместо этого нужно изменять входные данные до тех пор, пока не будет получен желаемый результат. (В этом и заключается назначение контрольной суммы – обнаруживать, если кто-то изменил выходные данные, так как эти правки будут потеряны при следующей генерации нового вывода Argument Clinic.)6768Для ясности приведём терминологию, которую мы будем использовать с Argument Clinic:6970- Первая строка комментария (`/*[clinic input]`) – это *строка начала*.71- Последняя строка начального комментария (`[clinic start generated code]*/`) – это *строка конца*.72- Последняя строка (`/*[clinic end generated code: checksum=...]*/`) – это *строка контрольной суммы*.73- Между начальной строкой и конечной строкой находится *входные данные*.74- Между конечной строкой и строкой контрольной суммы находится *выходные данные*.75- Весь текст в совокупности, от начальной строки до строки контрольной суммы включительно, является *блоком*. (Блок, который ещё не был успешно обработан Argument Clinic, не имеет выходных данных или строки контрольной суммы, но всё равно считается блоком.)7677## Преобразование вашей первой функции7879Лучший способ понять, как работает Argument Clinic, – преобразовать функцию для работы с ним. Итак, ниже приведены минимальные шаги, которые необходимо выполнить для преобразования функции для работы с Argument Clinic. Обратите внимание, что для кода, который вы планируете включить в CPython, стоит пойти дальше, используя некоторые продвинутые концепции, которые вы увидите далее в документе (например, «конвертеры возврата» и «конвертеры self»). Но в этом пошаговом руководстве мы будем придерживаться простого подхода, чтобы вы могли освоиться.8081Приступим!82830. Убедитесь, что вы работаете со свежеобновлённой копией основной ветки CPython.841. Найдите встроенную функцию Python, которая вызывает либо [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple), либо [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), и ещё не была переведена на работу с Argument Clinic. В качестве примера я использую `_pickle.Pickler.dump()`.852. Если вызов функции `PyArg_Parse` использует любой из следующих форматных юнитов:8687 ```python88 O&89 O!90 es91 es#92 et93 et#94 ```9596 или если в ней встречается несколько вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple), следует выбрать другую функцию. Argument Clinic *действительно* поддерживает все эти сценарии. Но это сложные темы – давайте сделаем что-то попроще для вашей первой функции.9798 Кроме того, если функция содержит несколько вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) или [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), причём поддерживаются разные типы для одного и того же аргумента, или если функция использует для разбора аргументов что-то отличное от функций PyArg\_Parse, то она, вероятно, не подходит для перевода на Argument Clinic. Argument Clinic не поддерживает обобщённые функции или полиморфные параметры.993. Добавьте следующий шаблонный код над функцией, создав наш блок:100101 ```python102 /*[clinic input]103 [clinic start generated code]*/104 ```1054. Вырежьте docstring и вставьте его между строками `[clinic]`, удалив весь мусор, который делает из него правильно оформленную C-строку. Когда закончите, у вас должен остаться только текст, выровненный по левому краю, без строк длиннее 80 символов. (Argument Clinic сохранит отступы внутри docstring.)106107 Если старый docstring содержал первую строку, похожую на сигнатуру функции, отбросьте эту строку. (Docstring больше в ней не нуждается – при использовании `help()` для вашей встроенной функции в будущем первая строка будет сгенерирована автоматически на основе сигнатуры функции.)108109 Пример:110111 ```python112 /*[clinic input]113 Write a pickled representation of obj to the open file.114 [clinic start generated code]*/115 ```1165. Если в строке документации нет строки «краткого описания», Argument Clinic выдаст предупреждение. Поэтому убедитесь, что она есть. Строка краткого описания должна представлять собой абзац из одной строки длиной до 80 символов в начале строки документации.117118 (В нашем примере строка документации состоит только из строки краткого описания, поэтому образец кода не нужно менять на этом шаге.)1196. Над docstring введите имя функции, затем пустую строку. Это должно быть Python-имя функции, полный путь через точку до функции – оно должно начинаться с имени модуля, включать все вложенные модули, а если функция является методом класса, то также должно содержать имя класса.120121 Пример:122123 ```python124 /*[clinic input]125 _pickle.Pickler.dump126127 Write a pickled representation of obj to the open file.128 [clinic start generated code]*/129 ```1307. Если этот модуль или класс впервые используются с Argument Clinic в данном C-файле, необходимо объявить модуль и/или класс. Правила гигиены Argument Clinic рекомендуют объявлять их в отдельном блоке где-нибудь ближе к началу C-файла, так же как файлы включений и статические переменные размещаются в начале. (В нашем примере мы просто покажем два блока рядом.)131132 Имя класса и модуля должно совпадать с тем, что видит Python. Проверьте имя, определённое в [`PyModuleDef`](https://python-all.ru/3.4/c-api/module.html#c.PyModuleDef) или [`PyTypeObject`](https://python-all.ru/3.4/c-api/type.html#c.PyTypeObject), в зависимости от ситуации.133134 При объявлении класса необходимо также указать два аспекта его типа в C: объявление типа, которое используется для указателя на экземпляр этого класса, и указатель на [`PyTypeObject`](https://python-all.ru/3.4/c-api/type.html#c.PyTypeObject) для этого класса.135136 Пример:137138 ```python139 /*[clinic input]140 module _pickle141 class _pickle.Pickler "PicklerObject *" "&Pickler_Type"142 [clinic start generated code]*/143144 /*[clinic input]145 _pickle.Pickler.dump146147 Write a pickled representation of obj to the open file.148 [clinic start generated code]*/149 ```1508. Объявите каждый параметр функции. Каждый параметр должен быть на отдельной строке. Все строки параметров должны быть с отступом относительно имени функции и строки документации.151152 Общий вид этих строк параметров следующий:153154 ```python155 name_of_parameter: converter156 ```157158 Если у параметра есть значение по умолчанию, добавьте его после конвертера:159160 ```python161 name_of_parameter: converter = default_value162 ```163164 Поддержка значений по умолчанию в Argument Clinic довольно сложная; обратитесь к [*разделу ниже о значениях по умолчанию*](https://python-all.ru/3.4/howto/clinic.html#default-values) для получения дополнительной информации.165166 Добавьте пустую строку под параметрами.167168 Что такое «конвертер»? Он задаёт как тип переменной, используемой в C, так и метод преобразования значения Python в значение C во время выполнения. Пока что вы будете использовать так называемый «унаследованный конвертер» (legacy converter) – удобный синтаксис, предназначенный для упрощения переноса старого кода в Argument Clinic.169170 Для каждого параметра скопируйте «форматный юнит» для этого параметра из аргумента формата `PyArg_Parse()` и укажите *его* в качестве конвертера в виде строки в кавычках. («Форматный юнит» – это официальное название подстроки из одного-трёх символов в параметре `format`, которая сообщает функции разбора аргументов, каков тип переменной и как её преобразовать. Дополнительную информацию о форматных юнитах см. в [*Parsing arguments and building values*](https://python-all.ru/3.4/c-api/arg.html#arg-parsing).)171172 Для многозначных форматных юнитов, таких как `z#`, используйте полную строку из двух-трёх символов.173174 Пример:175176 ```python177 /*[clinic input]178 module _pickle179 class _pickle.Pickler "PicklerObject *" "&Pickler_Type"180 [clinic start generated code]*/181182 /*[clinic input]183 _pickle.Pickler.dump184185 obj: 'O'186187 Write a pickled representation of obj to the open file.188 [clinic start generated code]*/189 ```1909. Если в строке формата вашей функции есть `|`, что означает, что некоторые параметры имеют значения по умолчанию, можете игнорировать его. Argument Clinic определяет, какие параметры необязательны, на основе наличия значений по умолчанию.191192 Если в строке формата вашей функции есть `$`, что означает, что она принимает только именованные аргументы, укажите `*` на отдельной строке перед первым именованным аргументом, с отступом, как у строк параметров.193194 (У `_pickle.Pickler.dump` нет ни того, ни другого, поэтому наш пример остаётся без изменений.)19510. Если существующая C-функция вызывает [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) (в отличие от [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords)), то все её аргументы являются только позиционными.196197 Чтобы пометить все параметры как только позиционные в Argument Clinic, добавьте `/` на отдельной строке после последнего параметра, с отступом, как у строк параметров.198199 Сейчас это работает по принципу «всё или ничего»: либо все параметры только позиционные, либо ни один. (В будущем Argument Clinic может ослабить это ограничение.)200201 Пример:202203 ```python204 /*[clinic input]205 module _pickle206 class _pickle.Pickler "PicklerObject *" "&Pickler_Type"207 [clinic start generated code]*/208209 /*[clinic input]210 _pickle.Pickler.dump211212 obj: 'O'213 /214215 Write a pickled representation of obj to the open file.216 [clinic start generated code]*/217 ```21811. Полезно писать докстринг на каждый параметр. Но такие докстринги необязательны – при желании этот шаг можно пропустить.219220 Вот как добавить докстринг для параметра. Первая строка этого докстринга должна иметь отступ больше, чем определение параметра. Левый отступ этой первой строки задаёт левый отступ для всего докстринга параметра; весь написанный текст будет сдвинут влево на эту величину. Можно писать сколько угодно текста, в том числе на нескольких строках.221222 Пример:223224 ```python225 /*[clinic input]226 module _pickle227 class _pickle.Pickler "PicklerObject *" "&Pickler_Type"228 [clinic start generated code]*/229230 /*[clinic input]231 _pickle.Pickler.dump232233 obj: 'O'234 The object to be pickled.235 /236237 Write a pickled representation of obj to the open file.238 [clinic start generated code]*/239 ```24012. Сохраните и закройте файл, затем запустите на нём `Tools/clinic/clinic.py`. Если повезёт, всё сработает, и в вашем блоке появится выходные данные! Откройте файл снова в текстовом редакторе, чтобы увидеть:241242 ```python243 /*[clinic input]244 module _pickle245 class _pickle.Pickler "PicklerObject *" "&Pickler_Type"246 [clinic start generated code]*/247 /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/248249 /*[clinic input]250 _pickle.Pickler.dump251252 obj: 'O'253 The object to be pickled.254 /255256 Write a pickled representation of obj to the open file.257 [clinic start generated code]*/258259 PyDoc_STRVAR(_pickle_Pickler_dump__doc__,260 "Write a pickled representation of obj to the open file.\n"261 "\n"262 ...263 static PyObject *264 _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)265 /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/266 ```267268 Очевидно, если Argument Clinic не выдал никакого вывода, значит, он нашёл ошибку во входных данных. Исправляйте ошибки и повторяйте запуск, пока Argument Clinic не обработает файл без нареканий.26913. Перепроверьте, что сгенерированный Argument Clinic код разбора аргументов в основном совпадает с существующим кодом.270271 Во-первых, убедитесь, что в обоих местах используется одна и та же функция разбора аргументов. Существующий код должен вызывать либо [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple), либо [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords); убедитесь, что код, сгенерированный Argument Clinic, вызывает *ту же самую* функцию.272273 Во-вторых, строка формата, передаваемая в [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) или [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), должна быть *в точности* такой же, как и написанная вручную в существующей функции, вплоть до двоеточия или точки с запятой.274275 (Argument Clinic всегда генерирует свои строки формата с `:`, за которым следует имя функции. Если строка формата существующего кода заканчивается на `;`, чтобы предоставить справку об использовании, это изменение безвредно – не беспокойтесь об этом.)276277 В-третьих, для параметров, единицы формата которых требуют двух аргументов (например, переменная длины, строка кодировки или указатель на функцию преобразования), убедитесь, что второй аргумент *в точности* одинаков в обоих вызовах.278279 В-четвёртых, внутри выходной части блока вы найдёте макрос препроцессора, определяющий соответствующую статическую структуру [`PyMethodDef`](https://python-all.ru/3.4/c-api/structures.html#c.PyMethodDef) для этой встроенной функции:280281 ```python282 #define __PICKLE_PICKLER_DUMP_METHODDEF \283 {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},284 ```285286 Эта статическая структура должна быть *в точности* такой же, как существующая статическая структура [`PyMethodDef`](https://python-all.ru/3.4/c-api/structures.html#c.PyMethodDef) для этой встроенной функции.287288 Если какие-либо из этих пунктов *хоть в чём-то* различаются, измените спецификацию функции Argument Clinic и повторно запустите `Tools/clinic/clinic.py`, пока они *не станут* одинаковыми.28914. Обратите внимание, что последняя строка вывода – это объявление вашей функции «impl». Сюда помещается реализация встроенной функции. Удалите существующий прототип изменяемой функции, но оставьте открывающую фигурную скобку. Затем удалите код разбора её аргументов и объявления всех переменных, в которые они помещались. Обратите внимание, что аргументы Python теперь стали аргументами этой функции impl; если в реализации использовались другие имена для этих переменных, исправьте это.290291 Повторим ещё раз, просто потому что это необычно. Теперь ваш код должен выглядеть так:292293 ```python294 static return_type295 your_function_impl(...)296 /*[clinic end generated code: checksum=...]*/297 {298 ...299 ```300301 Argument Clinic сгенерировал строку контрольной суммы и прототип функции прямо над ней. Вам нужно написать открывающую (и закрывающую) фигурные скобки для функции и реализацию внутри.302303 Пример:304305 ```python306 /*[clinic input]307 module _pickle308 class _pickle.Pickler "PicklerObject *" "&Pickler_Type"309 [clinic start generated code]*/310 /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/311312 /*[clinic input]313 _pickle.Pickler.dump314315 obj: 'O'316 The object to be pickled.317 /318319 Write a pickled representation of obj to the open file.320 [clinic start generated code]*/321322 PyDoc_STRVAR(__pickle_Pickler_dump__doc__,323 "Write a pickled representation of obj to the open file.\n"324 "\n"325 ...326 static PyObject *327 _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)328 /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/329 {330 /* Check whether the Pickler was initialized correctly (issue3664).331 Developers often forget to call __init__() in their subclasses, which332 would trigger a segfault without this check. */333 if (self->write == NULL) {334 PyErr_Format(PicklingError,335 "Pickler.__init__() was not called by %s.__init__()",336 Py_TYPE(self)->tp_name);337 return NULL;338 }339340 if (_Pickler_ClearBuffer(self) < 0)341 return NULL;342343 ...344 ```34515. Помните макрос со структурой [`PyMethodDef`](https://python-all.ru/3.4/c-api/structures.html#c.PyMethodDef) для этой функции? Найдите существующую структуру [`PyMethodDef`](https://python-all.ru/3.4/c-api/structures.html#c.PyMethodDef) для этой функции и замените её ссылкой на макрос. (Если встроенная функция находится на уровне модуля, она, вероятно, будет расположена совсем рядом с концом файла; если это метод класса, то, вероятно, ниже, но относительно близко к реализации.)346347 Обратите внимание, что тело макроса содержит завершающую запятую. Поэтому при замене существующей статической структуры [`PyMethodDef`](https://python-all.ru/3.4/c-api/structures.html#c.PyMethodDef) на макрос *не добавляйте* запятую в конце.348349 Пример:350351 ```python352 static struct PyMethodDef Pickler_methods[] = {353 __PICKLE_PICKLER_DUMP_METHODDEF354 __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF355 {NULL, NULL} /* sentinel */356 };357 ```35816. Скомпилируйте, затем запустите соответствующие части набора регрессионных тестов. Это изменение не должно приводить к появлению новых предупреждений или ошибок во время компиляции, и не должно вызывать видимых извне изменений в поведении Python.359360 Ну, за исключением одного отличия: `inspect.signature()`, вызванная для вашей функции, теперь должна возвращать корректную сигнатуру!361362 Поздравляем, вы портировали свою первую функцию для работы с Argument Clinic!363364## Продвинутые темы365366Теперь, когда у вас есть некоторый опыт работы с Argument Clinic, пора перейти к продвинутым темам.367368### Символические значения по умолчанию369370Значение по умолчанию для параметра не может быть произвольным выражением. В настоящее время явно поддерживаются следующие:371372- Числовые константы (целые и с плавающей запятой)373- Строковые константы374- `True`, `False` и `None`375- Простые символические константы, такие как `sys.maxsize`, которые должны начинаться с имени модуля376377Если интересно, это реализовано в `from_builtin()` в `Lib/inspect.py`.378379(В будущем, возможно, потребуется ещё более сложная логика, чтобы поддерживать полноценные выражения вроде `CONSTANT - 1`.)380381### Переименование C-функций и переменных, сгенерированных Argument Clinic382383Argument Clinic автоматически присваивает имена создаваемым функциям. Иногда это может вызвать проблему, если сгенерированное имя совпадает с именем существующей C-функции. Есть простое решение: переопределите имена, используемые для C-функций. Просто добавьте ключевое слово `"as"` в строку объявления функции, а затем укажите нужное имя функции. Argument Clinic будет использовать это имя для базовой (сгенерированной) функции, а затем добавит `"_impl"` в конец и использует его для имени impl-функции.384385Например, если бы мы захотели переименовать имена C-функций, сгенерированных для `pickle.Pickler.dump`, это выглядело бы так:386387```python388/*[clinic input]389pickle.Pickler.dump as pickler_dumper390391...392```393394Базовая функция теперь будет называться `pickler_dumper()`, а impl-функция – `pickler_dumper_impl()`.395396Аналогично, может возникнуть ситуация, когда нужно дать параметру определённое имя в Python, но это имя может быть неудобным в C. Argument Clinic позволяет задать параметру разные имена в Python и C, используя тот же синтаксис `"as"`:397398```python399/*[clinic input]400pickle.Pickler.dump401402 obj: object403 file as file_obj: object404 protocol: object = NULL405 *406 fix_imports: bool = True407```408409Здесь имя, используемое в Python (в сигнатуре и массиве `keywords`), будет `file`, а C-переменная будет называться `file_obj`.410411Это можно использовать и для переименования параметра `self`!412413### Преобразование функций, использующих PyArg\_UnpackTuple414415Чтобы преобразовать функцию, разбирающую свои аргументы с помощью [`PyArg_UnpackTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_UnpackTuple), просто перечислите все аргументы, указав каждый как `object`. Вы можете указать аргумент `type` для приведения типа по необходимости. Все аргументы должны быть помечены как позиционные (добавьте `/` на отдельной строке после последнего аргумента).416417В настоящее время сгенерированный код будет использовать [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple), но это скоро изменится.418419### Опциональные группы420421Некоторые устаревшие функции используют хитрый подход к разбору аргументов: они подсчитывают количество позиционных аргументов, а затем используют оператор `switch`, чтобы вызвать один из нескольких различных вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) в зависимости от того, сколько позиционных аргументов передано. (Эти функции не могут принимать только ключевые аргументы.) Этот подход использовался для имитации необязательных аргументов до того, как была создана [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords).422423Хотя функции, использующие этот подход, часто можно преобразовать для использования [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), необязательных аргументов и значений по умолчанию, это возможно не всегда. Некоторые из этих устаревших функций имеют поведение, которое [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) напрямую не поддерживает. Самый очевидный пример – встроенная функция `range()`, у которой необязательный аргумент находится *слева* от обязательного! Другой пример – `curses.window.addch()`, у которой есть группа из двух аргументов, которые всегда должны указываться вместе. (Аргументы называются `x` и `y`; если вы вызываете функцию, передавая `x`, вы должны также передать `y` – а если вы не передаёте `x`, вы не можете передавать и `y`.)424425В любом случае, цель Argument Clinic – поддерживать разбор аргументов для всех существующих встроенных функций CPython без изменения их семантики. Поэтому Argument Clinic поддерживает этот альтернативный подход к разбору, используя так называемые *опциональные группы*. Опциональные группы – это группы аргументов, которые должны передаваться вместе. Они могут находиться слева или справа от обязательных аргументов. Их можно использовать *только* с только позиционными параметрами.426427> **Примечание**428>429> Необязательные группы *предназначены только* для использования при преобразовании функций, которые делают несколько вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple)! Функции, использующие *любой другой* подход к разбору аргументов, *почти никогда* не следует преобразовывать в Argument Clinic с помощью необязательных групп. Функции, использующие необязательные группы, в настоящее время не могут иметь точные сигнатуры в Python, потому что Python просто не понимает этой концепции. Пожалуйста, избегайте использования необязательных групп везде, где это возможно.430431Чтобы задать необязательную группу, добавьте `[` на отдельной строке перед параметрами, которые нужно сгруппировать, и `]` на отдельной строке после этих параметров. В качестве примера, вот как `curses.window.addch` использует необязательные группы, чтобы сделать первые два параметра и последний параметр необязательными:432433```python434/*[clinic input]435436curses.window.addch437438 [439 x: int440 X-coordinate.441 y: int442 Y-coordinate.443 ]444445 ch: object446 Character to add.447448 [449 attr: long450 Attributes for the character.451 ]452 /453454...455```456457Примечания:458459- Для каждой необязательной группы в impl-функцию будет передан один дополнительный параметр, представляющий эту группу. Параметр будет иметь тип int и имя `group_{direction}_{number}`, где `{direction}` может быть `right` или `left` в зависимости от того, находится ли группа до или после обязательных параметров, а `{number}` – монотонно возрастающее число (начиная с 1), указывающее, насколько далеко группа находится от обязательных параметров. При вызове impl этот параметр будет равен нулю, если группа не использовалась, и не равен нулю, если группа использовалась. (Под использованием или неиспользованием подразумевается, получили ли параметры аргументы в данном вызове.)460- Если обязательных аргументов нет, опциональные группы будут вести себя так, как если бы они находились справа от обязательных аргументов.461- В случае неоднозначности код разбора аргументов отдаёт предпочтение параметрам слева (перед обязательными параметрами).462- Опциональные группы могут содержать только позиционные параметры.463- Опциональные группы *предназначены только* для устаревшего кода. Пожалуйста, не используйте опциональные группы в новом коде.464465### Использование настоящих конвертеров Argument Clinic вместо «устаревших конвертеров»466467Чтобы сэкономить время и минимизировать объём знаний, необходимых для первого переноса кода на Argument Clinic, в приведённом выше пошаговом руководстве рекомендуется использовать «устаревшие конвертеры». «Устаревшие конвертеры» – это удобство, созданное специально для упрощения переноса существующего кода на Argument Clinic. И, чтобы было понятно, их использование допустимо при переносе кода для Python 3.4.468469Однако в долгосрочной перспективе, вероятно, желательно, чтобы все наши блоки использовали настоящий синтаксис конвертеров Argument Clinic. Почему? Несколько причин:470471- Правильные конвертеры гораздо легче читать, и их назначение понятнее.472- Существуют некоторые форматные единицы, которые не поддерживаются в качестве «устаревших конвертеров», поскольку они требуют аргументов, а синтаксис устаревших конвертеров не поддерживает указание аргументов.473- В будущем у нас может появиться новая библиотека разбора аргументов, которая не ограничена тем, что поддерживает [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple); эта гибкость не будет доступна параметрам, использующим устаревшие конвертеры.474475Поэтому, если вы не против небольшого дополнительного усилия, пожалуйста, используйте обычные конвертеры вместо устаревших.476477Если кратко, синтаксис конвертеров Argument Clinic (не устаревших) выглядит как вызов функции Python. Однако, если у функции нет явных аргументов (все функции принимают значения по умолчанию), скобки можно опустить. Таким образом, `bool` и `bool()` – это абсолютно одинаковые конвертеры.478479Все аргументы конвертеров Argument Clinic являются только ключевыми (keyword-only). Все конвертеры Argument Clinic принимают следующие аргументы:480481> **`c_default`**482>483> Значение по умолчанию для этого параметра при определении в C. А именно, это будет инициализатор переменной, объявленной в «функции разбора» (parse function). См.484>485> [*раздел о значениях по умолчанию*](https://python-all.ru/3.4/howto/clinic.html#default-values)486>487> , чтобы узнать, как это использовать. Указывается в виде строки.488>489> **`аннотация`**490>491> Значение аннотации для этого параметра. В настоящее время не поддерживается, поскольку PEP 8 требует, чтобы библиотека Python не использовала аннотации.492493Кроме того, некоторые конвертеры принимают дополнительные аргументы. Вот список этих аргументов с их значениями:494495> **`bitwise`**496>497> Поддерживается только для беззнаковых целых чисел. Собственное целочисленное значение этого аргумента Python будет записано в параметр без какой-либо проверки диапазона, даже для отрицательных значений.498>499> **`converter`**500>501> Поддерживается только конвертером502>503> `object`504>505> . Указывает имя506>507> [*C-«функции-конвертера»*](https://python-all.ru/3.4/c-api/arg.html#o-ampersand)508>509> , которая будет использоваться для преобразования этого объекта в собственный тип.510>511> **`encoding`**512>513> Поддерживается только для строк. Указывает кодировку, которую следует использовать при преобразовании этой строки из значения Python str (Unicode) в значение C514>515> `char *`516>517> .518>519> **`length`**520>521> Поддерживается только для строк. Если true, запрашивает передачу длины строки в impl-функцию сразу после параметра строки, в параметре с именем522>523> `<parameter_name>_length`524>525> .526>527> **`nullable`**528>529> Поддерживается только для строк. Если true, этот параметр также может быть установлен в530>531> `None`532>533> , и в этом случае C-параметр будет установлен в534>535> `NULL`536>537> .538>539> **`subclass_of`**540>541> Поддерживается только для конвертера542>543> `object`544>545> . Требует, чтобы значение Python было подклассом типа Python, выраженным в C.546>547> **`types`**548>549> Поддерживается только для конвертера550>551> `object`552>553> (и554>555> `self`556>557> ). Указывает тип C, используемый для объявления переменной. Значение по умолчанию –558>559> `"PyObject *"`560>561> .562>563> **`types`**564>565> Строка, содержащая список типов Python (и, возможно, псевдотипов); она ограничивает допустимые аргументы Python значениями этих типов. (Это не универсальный механизм; как правило, он поддерживает только определённые списки типов, приведённые в таблице устаревших конвертеров.)566>567> **`zeroes`**568>569> Поддерживается только для строк. Если true, внутри значения допускаются встроенные NUL-байты (570>571> `'\\0'`572>573> ).574575Имейте в виду, что не любое сочетание аргументов будет работать. Часто эти аргументы реализованы внутри с помощью определённых `PyArg_ParseTuple` *форматных символов* с определённым поведением. Например, сейчас нельзя вызвать `str` и передать `zeroes=True` без указания `encoding`; хотя вполне разумно думать, что это сработает, такая семантика не соответствует ни одному существующему форматному символу. Поэтому Argument Clinic этого не поддерживает. (Или, по крайней мере, пока нет.)576577Ниже приведена таблица, показывающая соответствие устаревших конвертеров настоящим конвертерам Argument Clinic. Слева – устаревший конвертер, справа – текст, на который его следует заменить.578579| `'B'` | `unsigned_char(bitwise=True)` |580| --- | --- |581| `'b'` | `unsigned_char` |582| `'c'` | `char` |583| `'C'` | `int(types='str')` |584| `'d'` | `double` |585| `'D'` | `Py_complex` |586| `'es#'` | `str(encoding='name_of_encoding', length=True, zeroes=True)` |587| `'es'` | `str(encoding='name_of_encoding')` |588| `'et#'` | `str(encoding='name_of_encoding', types='bytes bytearray str', length=True)` |589| `'et'` | `str(encoding='name_of_encoding', types='bytes bytearray str')` |590| `'f'` | `float` |591| `'h'` | `short` |592| `'H'` | `unsigned_short(bitwise=True)` |593| `'i'` | `int` |594| `'I'` | `unsigned_int(bitwise=True)` |595| `'k'` | `unsigned_long(bitwise=True)` |596| `'K'` | `unsigned_PY_LONG_LONG(bitwise=True)` |597| `'L'` | `PY_LONG_LONG` |598| `'n'` | `Py_ssize_t` |599| `'O!'` | `object(subclass_of='&PySomething_Type')` |600| `'O&'` | `object(converter='name_of_c_function')` |601| `'O'` | `object` |602| `'p'` | `bool` |603| `'s#'` | `str(length=True)` |604| `'S'` | `PyBytesObject` |605| `'s'` | `str` |606| `'s*'` | `Py_buffer(types='str bytes bytearray buffer')` |607| `'u#'` | `Py_UNICODE(length=True)` |608| `'u'` | `Py_UNICODE` |609| `'U'` | `unicode` |610| `'w*'` | `Py_buffer(types='bytearray rwbuffer')` |611| `'y#'` | `str(types='bytes', length=True)` |612| `'Y'` | `PyByteArrayObject` |613| `'y'` | `str(types='bytes')` |614| `'y*'` | `Py_buffer` |615| `'Z#'` | `Py_UNICODE(nullable=True, length=True)` |616| `'z#'` | `str(nullable=True, length=True)` |617| `'Z'` | `Py_UNICODE(nullable=True)` |618| `'z'` | `str(nullable=True)` |619| `'z*'` | `Py_buffer(types='str bytes bytearray buffer', nullable=True)` |620621В качестве примера приведём наш образец `pickle.Pickler.dump`, использующий правильный конвертер:622623```python624/*[clinic input]625pickle.Pickler.dump626627 obj: object628 The object to be pickled.629 /630631Write a pickled representation of obj to the open file.632[clinic start generated code]*/633```634635Argument Clinic покажет все доступные конвертеры. Для каждого конвертера будут показаны все принимаемые им параметры и значения по умолчанию. Чтобы увидеть полный список, запустите `Tools/clinic/clinic.py --converters`.636637### Py\_buffer638639При использовании конвертера `Py_buffer` (или устаревших конвертеров `'s*'`, `'w*'`, `'*y'`, `'z*'`) *нельзя* вызывать [`PyBuffer_Release()`](https://python-all.ru/3.4/c-api/buffer.html#c.PyBuffer_Release) для предоставленного буфера. Argument Clinic генерирует код, который делает это автоматически (в функции разбора).640641### Продвинутые конвертеры642643Помните те форматные единицы, которые вы пропустили в первый раз, потому что они были сложными? Вот как с ними работать.644645Дело в том, что все эти строки формата принимают аргументы – либо функции преобразования, либо типы, либо строки, задающие кодировку. (Но «устаревшие конвертеры» аргументов не поддерживают. Поэтому в первой функции они не рассматривались.) Аргумент, указанный для строки формата, теперь является аргументом конвертера; этот аргумент – либо `converter` (для `O&`), `subclass_of` (для `O!`), либо `encoding` (для всех строк формата, начинающихся с `e`).646647При использовании `subclass_of` может также потребоваться другой пользовательский аргумент для `object()`: `type`, который позволяет задать тип, фактически используемый для параметра. Например, если нужно гарантировать, что объект является подклассом `PyUnicode_Type`, вероятно, следует использовать конвертер `object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')`.648649Одна из возможных проблем при использовании Argument Clinic: он лишает некоторой гибкости строки формата, начинающиеся с `e`. При написании вызова `PyArg_Parse` вручную можно было бы теоретически решить во время выполнения, какую строку кодировки передать [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple). Но теперь эта строка должна быть жёстко задана на этапе предварительной обработки Argument Clinic. Это ограничение сделано намеренно: оно значительно упростило поддержку этой строки формата и может позволить будущие оптимизации. Это ограничение не кажется необоснованным; сам CPython всегда передаёт статические жёстко заданные строки кодировки для параметров, строки формата которых начинаются с `e`.650651### Значения по умолчанию параметров652653Значения по умолчанию параметров могут быть различными. В простейшем случае это могут быть строковые, целочисленные литералы или литералы с плавающей точкой:654655```python656foo: str = "abc"657bar: int = 123658bat: float = 45.6659```660661Также могут использоваться любые встроенные константы Python:662663```python664yep: bool = True665nope: bool = False666nada: object = None667```668669Также предусмотрена специальная поддержка значения по умолчанию `NULL` и простых выражений, описанная в следующих разделах.670671### Значение по умолчанию `NULL`672673Для строковых параметров и параметров-объектов можно задать значение `None`, чтобы указать, что значение по умолчанию отсутствует. Однако это означает, что переменная C будет инициализирована значением `Py_None`. Для удобства существует специальное значение `NULL`: с точки зрения Python оно ведёт себя как значение по умолчанию `None`, но переменная C инициализируется значением `NULL`.674675### Выражения, задаваемые как значения по умолчанию676677Значение по умолчанию для параметра может быть не просто литералом. Это может быть целое выражение, использующее математические операторы и обращающееся к атрибутам объектов. Однако эта поддержка не совсем проста из-за некоторых неочевидных семантических нюансов.678679Рассмотрим следующий пример:680681```python682foo: Py_ssize_t = sys.maxsize - 1683```684685`sys.maxsize` может иметь разные значения на разных платформах. Поэтому Argument Clinic не может просто вычислить это выражение локально и жёстко закодировать в C. Вместо этого он сохраняет значение по умолчанию так, чтобы оно вычислялось во время выполнения, когда пользователь запрашивает сигнатуру функции.686687Какое пространство имён доступно при вычислении выражения? Оно вычисляется в контексте модуля, из которого взята встроенная функция. Так что, если в вашем модуле есть атрибут с именем «`max_widgets`», вы можете просто использовать его:688689```python690foo: Py_ssize_t = max_widgets691```692693Если символ не найден в текущем модуле, поиск продолжается в `sys.modules`. Именно так находится, например, `sys.maxsize`. (Поскольку заранее неизвестно, какие модули пользователь загрузит в свой интерпретатор, лучше ограничиться модулями, которые предзагружены самим Python.)694695Вычисление значений по умолчанию только во время выполнения означает, что Argument Clinic не может вычислить правильный эквивалент значения по умолчанию в C. Поэтому нужно указать его явно. При использовании выражения необходимо также задать эквивалентное выражение в C с помощью параметра `c_default` конвертера:696697```python698foo: Py_ssize_t(c_default="PY_SSIZE_T_MAX - 1") = sys.maxsize - 1699```700701Ещё одно осложнение: Argument Clinic не может заранее узнать, является ли предоставленное выражение корректным. Он разбирает его, чтобы убедиться, что оно выглядит допустимым, но не может *на самом деле* знать. Нужно быть очень осторожным при использовании выражений для указания значений, которые гарантированно будут корректными во время выполнения!702703Наконец, поскольку выражения должны быть представимы как статические значения C, на допустимые выражения накладывается много ограничений. Вот список возможностей Python, которые использовать запрещено:704705- Вызовы функций.706- Инлайн-выражения `3 if foo else 5`.707- Автоматическая распаковка последовательностей (`*[1, 2, 3]`).708- Генераторы списков, множеств, словарей и выражения-генераторы.709- Литералы кортежей, списков, множеств, словарей.710711### Использование конвертера возвращаемого значения712713По умолчанию функция impl, которую генерирует для вас Argument Clinic, возвращает `PyObject *`. Но ваша функция на C часто вычисляет какой-то тип C, а затем преобразует его в `PyObject *` в последний момент. Argument Clinic преобразует ваши входные данные из типов Python в нативные типы C – почему бы не поручить ему и преобразование возвращаемого значения из нативного типа C в тип Python?714715Именно это и делает «конвертер возвращаемого значения». Он меняет вашу функцию impl так, чтобы она возвращала некоторый тип C, а затем добавляет в сгенерированную (не impl) функцию код для преобразования этого значения в соответствующий `PyObject *`.716717Синтаксис конвертеров возвращаемого значения аналогичен синтаксису конвертеров параметров. Конвертер возвращаемого значения указывается как аннотация возврата самой функции. Конвертеры возвращаемого значения ведут себя почти так же, как конвертеры параметров: они принимают аргументы, все аргументы являются только ключевыми, и если ни один из аргументов по умолчанию не изменяется, скобки можно опустить.718719(Если вы используете в своей функции и `"as"` *и* конвертер возвращаемого значения, `"as"` должен идти до конвертера возвращаемого значения.)720721При использовании конвертеров возвращаемого значения возникает ещё одно усложнение: как указать на ошибку? Обычно функция возвращает корректный (не `NULL`) указатель при успехе и `NULL` при неудаче. Но если используется целочисленный конвертер возвращаемого значения, все целые числа являются корректными. Как же Argument Clinic может обнаружить ошибку? Решение: каждый конвертер возвращаемого значения неявно ищет специальное значение, указывающее на ошибку. Если вернуть это значение и при этом была установлена ошибка (`PyErr_Occurred()` возвращает истинное значение), то сгенерированный код распространит ошибку. В противном случае он закодирует возвращённое значение как обычно.722723В настоящее время Argument Clinic поддерживает лишь несколько конвертеров возвращаемого значения:724725```python726bool727int728unsigned int729long730unsigned int731size_t732Py_ssize_t733float734double735DecodeFSDefault736```737738Ни один из них не принимает параметров. Для первых трёх возвращайте -1 для указания ошибки. Для `DecodeFSDefault` возвращаемый тип – `char *`; возвращайте нулевой указатель для обозначения ошибки.739740(Существует также экспериментальный конвертер `NoneType`, который позволяет возвращать `Py_None` при успехе или `NULL` при неудаче, без увеличения счётчика ссылок на `Py_None`. Не уверен, что он добавляет достаточно ясности, чтобы его стоило использовать.)741742Чтобы увидеть все конвертеры возвращаемого значения, поддерживаемые Argument Clinic, вместе с их параметрами (если они есть), просто выполните `Tools/clinic/clinic.py --converters` для получения полного списка.743744### Клонирование существующих функций745746Если есть несколько функций, которые выглядят похоже, можно воспользоваться функцией «клонирования» в Clinic. При клонировании существующей функции повторно используются:747748- её параметры, включая749750 - их имена,751 - их конвертеры, со всеми параметрами,752 - их значения по умолчанию,753 - их документирующие строки для каждого параметра,754 - их *вид* (являются ли они только позиционными, позиционными или ключевыми, или только ключевыми), и755- её конвертер возвращаемого значения.756757Единственное, что не копируется из исходной функции, – это её документирующая строка; синтаксис позволяет указать новую документирующую строку.758759Вот синтаксис клонирования функции:760761```python762/*[clinic input]763module.class.new_function [as c_basename] = module.class.existing_function764765Docstring for new_function goes here.766[clinic start generated code]*/767```768769(Функции могут находиться в разных модулях или классах. Я написал `module.class` в примере только для иллюстрации того, что необходимо указывать полный путь к *обеим* функциям.)770771К сожалению, нет синтаксиса для частичного клонирования функции или клонирования функции с последующим её изменением. Клонирование – это принцип «всё или ничего».772773Также функция, с которой производится клонирование, должна быть ранее определена в текущем файле.774775### Вызов кода Python776777Для остальных продвинутых тем требуется написать код Python, который находится внутри C-файла и изменяет состояние времени выполнения Argument Clinic. Это просто: нужно всего лишь определить блок Python.778779Блок Python использует другие строки-разделители, нежели блок функции Argument Clinic. Выглядит это так:780781```python782/*[python input]783# Сюда помещается код Python.784[python start generated code]*/785```786787Весь код внутри блока Python выполняется в момент его разбора. Весь текст, выводимый в stdout внутри блока, перенаправляется в «вывод» после блока.788789В качестве примера приведён блок Python, который добавляет статическую целочисленную переменную в код C:790791```python792/*[python input]793print('static int __ignored_unused_variable__ = 0;')794[python start generated code]*/795static int __ignored_unused_variable__ = 0;796/*[python checksum:...]*/797```798799### Использование «конвертера self»800801Argument Clinic автоматически добавляет параметр «self» с помощью конвертера по умолчанию. Он автоматически устанавливает `type` этого параметра в «указатель на экземпляр», который вы указали при объявлении типа. Однако вы можете переопределить конвертер Argument Clinic и задать свой собственный. Просто добавьте свой параметр `self` в качестве первого параметра в блоке и убедитесь, что его конвертер является экземпляром `self_converter` или его подкласса.802803Зачем это нужно? Это позволяет переопределить тип `self` или задать ему другое имя по умолчанию.804805Как задать собственный тип, к которому нужно привести `self`? Если у вас всего одна-две функции с одинаковым типом для `self`, можно напрямую использовать существующий конвертер `self` Argument Clinic, передав нужный тип в качестве параметра `type`:806807```python808/*[clinic input]809810_pickle.Pickler.dump811812 self: self(type="PicklerObject *")813 obj: object814 /815816Write a pickled representation of the given object to the open file.817[clinic start generated code]*/818```819820С другой стороны, если у вас много функций, использующих один и тот же тип для `self`, лучше создать собственный конвертер, унаследовав его от `self_converter`, но переопределив член `type`:821822```python823/*[python input]824class PicklerObject_converter(self_converter):825 type = "PicklerObject *"826[python start generated code]*/827828/*[clinic input]829830_pickle.Pickler.dump831832 self: PicklerObject833 obj: object834 /835836Write a pickled representation of the given object to the open file.837[clinic start generated code]*/838```839840### Создание пользовательского конвертера841842Как мы уже намекали в предыдущем разделе... вы можете писать собственные конвертеры! Конвертер – это просто класс Python, наследующий от `CConverter`. Основное назначение пользовательского конвертера – работа с параметром, использующим форматную единицу `O&`: разбор такого параметра означает вызов «функции-конвертера» [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple).843844Ваш класс конвертера должен называться `*что-то*_converter`. Если имя соответствует этому соглашению, то класс конвертера будет автоматически зарегистрирован в Argument Clinic; его именем станет имя вашего класса с удалённым суффиксом `_converter`. (Это достигается с помощью метакласса.)845846Не следует переопределять `CConverter.__init__`. Вместо этого нужно написать функцию `converter_init()`. `converter_init()` всегда принимает параметр `self`; после него все дополнительные параметры *должны* быть только ключевыми. Любые аргументы, переданные конвертеру в Argument Clinic, будут переданы вашей `converter_init()`.847848В вашем подклассе можно указать некоторые дополнительные члены `CConverter`. Вот текущий список:849850**`type`**851852Тип C для этой переменной.853854`type`855856должна быть строкой Python, задающей тип, например857858`int`859860. Если это тип-указатель, строка типа должна заканчиваться на861862`' *'`863864.865866**`default`**867868Значение по умолчанию для этого параметра в Python, заданное как значение Python. Или магическое значение869870`unspecified`871872, если значение по умолчанию отсутствует.873874**`py_default`**875876`default`877878в том виде, в котором оно должно отображаться в коде Python, в виде строки. Или879880`None`881882, если значение по умолчанию отсутствует.883884**`c_default`**885886`default`887888в том виде, в котором оно должно отображаться в коде C, в виде строки. Или889890`None`891892, если значение по умолчанию отсутствует.893894**`c_ignored_default`**895896Значение по умолчанию, используемое для инициализации переменной C, когда значение по умолчанию отсутствует, но его отсутствие может привести к предупреждению «неинициализированная переменная». Это легко может произойти при использовании групп опций – хотя правильно написанный код никогда не будет использовать это значение, переменная всё равно передаётся в impl, и компилятор C будет жаловаться на «использование» неинициализированного значения. Это значение всегда должно быть непустой строкой.897898**`converter`**899900Имя функции-преобразователя C в виде строки.901902**`impl_by_reference`**903904Логическое значение. Если истинно, Argument Clinic добавит905906`&`907908перед именем переменной при передаче её в функцию impl.909910**`parse_by_reference`**911912Логическое значение. Если оно истинно, Argument Clinic добавит913914`&`915916перед именем переменной при передаче её в917918[`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple)919920.921922Вот простейший пример пользовательского преобразователя из `Modules/zlibmodule.c`:923924```python925/*[python input]926927class uint_converter(CConverter):928 type = 'unsigned int'929 converter = 'uint_converter'930931[python start generated code]*/932/*[python end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/933```934935Этот блок добавляет в Argument Clinic преобразователь с именем `uint`. Параметры, объявленные как `uint`, будут объявлены с типом `unsigned int` и будут разбираться с помощью форматной единицы `'O&'`, которая вызовет функцию-преобразователь `uint_converter`. `uint`-переменные автоматически поддерживают значения по умолчанию.936937Более сложные пользовательские преобразователи могут вставлять собственный код C для инициализации и очистки. Дополнительные примеры пользовательских преобразователей можно найти в исходном коде CPython; поищите в C-файлах строку `CConverter`.938939### Написание пользовательского преобразователя возвращаемого значения940941Написание пользовательского преобразователя возвращаемого значения во многом похоже на написание обычного пользовательского преобразователя, только несколько проще, потому что сами преобразователи возвращаемых значений намного проще.942943Преобразователи возвращаемых значений должны быть подклассами `CReturnConverter`. Примеров пользовательских преобразователей возвращаемых значений пока нет, потому что они ещё не получили широкого распространения. Если вы хотите написать собственный преобразователь возвращаемого значения, прочитайте `Tools/clinic/clinic.py`, особенно реализацию `CReturnConverter` и всех его подклассов.944945### METH\_O и METH\_NOARGS946947Чтобы преобразовать функцию, использующую `METH_O`, убедитесь, что её единственный аргумент использует преобразователь `object`, и пометьте аргументы как позиционные:948949```python950/*[clinic input]951meth_o_sample952953 argument: object954 /955[clinic start generated code]*/956```957958Чтобы преобразовать функцию, использующую `METH_NOARGS`, просто не указывайте никаких аргументов.959960По-прежнему можно использовать преобразователь self, преобразователь возвращаемого значения и указывать аргумент `type` для преобразователя object для `METH_O`.961962### Функции tp\_new и tp\_init963964Можно преобразовывать функции `tp_new` и `tp_init`. Просто называйте их `__new__` или `__init__` соответственно. Замечания:965966- Сгенерированное имя функции для `__new__` не заканчивается на `__new__`, как было бы по умолчанию. Это просто имя класса, преобразованное в допустимый идентификатор C.967- Для этих функций не генерируется `PyMethodDef` `#define`.968- Функции `__init__` возвращают `int`, а не `PyObject *`.969- Используйте строку документации как строку документации класса.970- Хотя функции `__new__` и `__init__` всегда должны принимать оба объекта `args` и `kwargs`, при преобразовании можно указать любую сигнатуру для этих функций. (Если ваша функция не поддерживает именованные аргументы, сгенерированная функция разбора выдаст исключение, если получит таковые.)971972### Изменение и перенаправление вывода Clinic973974Может быть неудобно, когда вывод Clinic перемежается с вашим обычным кодом C, отредактированным вручную. К счастью, Clinic настраивается: можно буферизовать его вывод для печати позже (или раньше!) или записывать вывод в отдельный файл. Также можно добавить префикс или суффикс к каждой строке сгенерированного вывода Clinic.975976Хотя изменение вывода Clinic таким образом может улучшить читаемость, оно может привести к тому, что код Clinic начнёт использовать типы до их определения, или ваш код попытается использовать сгенерированный Clinic код до его определения. Эти проблемы легко решаются перестановкой объявлений в файле или перемещением места, куда попадает сгенерированный код Clinic. (Вот почему поведение Clinic по умолчанию – выводить всё в текущий блок; хотя многие считают, что это ухудшает читаемость, это никогда не потребует перестановки кода для исправления проблем «использование до определения».)977978Начнём с определения некоторых терминов:979980***поле***981982Поле в данном контексте – это подраздел вывода Clinic. Например, `#define` для структуры `PyMethodDef` является полем, называемым `methoddef_define`. Clinic имеет семь различных полей, которые он может выводить для каждого определения функции:983984```python985docstring_prototype986docstring_definition987methoddef_define988impl_prototype989parser_prototype990parser_definition991impl_definition992```993994Все имена имеют форму `"<a>_<b>"`, где `"<a>"` – это семантический объект (функция разбора, функция реализации, строка документации или структура methoddef), а `"<b>"` представляет собой тип оператора (statement). Имена полей, оканчивающиеся на `"_prototype"`, обозначают предварительные объявления этой сущности без её фактического тела/данных; имена полей, оканчивающиеся на `"_definition"`, представляют фактическое определение сущности с её телом/данными. (`"methoddef"` особенный – это единственный, который оканчивается на `"_define"`, обозначая, что это препроцессорный #define.)995996***назначение***997998Назначение – это место, куда Clinic может записывать вывод. Существует пять встроенных назначений:9991000**`block`**10011002Назначение по умолчанию: выводится в секции вывода текущего блока Clinic.10031004**`buffer`**10051006Текстовый буфер, в который можно сохранять текст для последующего использования. Текст, отправленный сюда, добавляется в конец любого существующего текста. Ошибка – оставить любой текст в буфере, когда Clinic завершает обработку файла.10071008**`file`**10091010Отдельный «clinic-файл», который будет автоматически создан Clinic. Имя файла выбирается по шаблону `{basename}.clinic{extension}`, где `basename` и `extension` получены из результата `os.path.splitext()` для текущего файла. (Пример: назначение `file` для `_pickle.c` будет записано в `_pickle.clinic.c`.)10111012**Важно: при использовании** `file` **назначения** *необходимо включать в систему контроля версий* **сгенерированный файл!**10131014**`two-pass`**10151016Буфер, аналогичный10171018`buffer`10191020. Однако двухпроходный буфер можно записывать только один раз, и он выводит весь текст, отправленный в него в процессе обработки, даже из блоков Clinic10211022*после*10231024**`suppress`**10251026Текст подавляется – отбрасывается.10271028Clinic определяет пять новых директив, позволяющих перенастроить его вывод.10291030Первая новая директива – `dump`:10311032```python1033dump <destination>1034```10351036Она выгружает текущее содержимое указанного назначения в вывод текущего блока и очищает его. Работает только с назначениями `buffer` и `two-pass`.10371038Вторая новая директива – `output`. Самая простая форма `output` выглядит так:10391040```python1041output <field> <destination>1042```10431044Это указывает Clinic выводить *поле* в *назначение*. `output` также поддерживает специальное мета-назначение, называемое `everything`, которое указывает Clinic выводить *все* поля в это *назначение*.10451046`output` имеет ряд других функций:10471048```python1049output push1050output pop1051output preset <preset>1052```10531054`output push` и `output pop` позволяют помещать и извлекать конфигурации из внутреннего стека конфигураций, чтобы можно было временно изменить выходную конфигурацию, а затем легко восстановить предыдущую. Достаточно поместить текущую конфигурацию в стек перед изменением, а затем извлечь, когда потребуется восстановить предыдущую конфигурацию.10551056`output preset` устанавливает выход Clinic в одну из нескольких встроенных предустановленных конфигураций, как описано ниже:10571058> **`block`**1059>1060> Исходная начальная конфигурация Clinic. Пишет всё сразу после входного блока.1061>1062> Подавляет `parser_prototype` и `docstring_prototype`, всё остальное записывает в `block`.1063>1064> **`file`**1065>1066> Предназначен для записи всего возможного в «clinic file». Затем файл включается директивой `#include` в начале файла. Возможно, потребуется перестроить файл, чтобы это работало, хотя обычно это означает лишь создание предварительных объявлений для различных `typedef` и `PyTypeObject` определений.1067>1068> Подавляет `parser_prototype` и `docstring_prototype`, записывает `impl_definition` в `block`, а всё остальное записывает в `file`.1069>1070> Имя файла по умолчанию – `"{dirname}/clinic/{basename}.h"`.1071>1072> **`buffer`**1073>1074> Сохраняет почти весь вывод Clinic для последующей записи в файл ближе к концу. Для файлов Python, реализующих модули или встроенные типы, рекомендуется сбрасывать буфер прямо над статическими структурами модуля или встроенного типа; обычно они находятся в самом конце. Использование `buffer` может потребовать даже больше правок, чем `file`, если в файле есть статические массивы `PyMethodDef`, определённые в середине файла.1075>1076> Подавляет `parser_prototype`, `impl_prototype` и `docstring_prototype`, записывает `impl_definition` в `block`, а всё остальное записывает в `file`.1077>1078> **`two-pass`**1079>1080> Аналогичен предустановке `buffer`, но записывает предварительные объявления в буфер `two-pass`, а определения – в буфер `buffer`. Это похоже на предустановку `buffer`, но может потребовать меньше правок, чем `buffer`. Сбросьте буфер `two-pass` ближе к началу файла, а буфер `buffer` – ближе к концу, как и при использовании предустановки `buffer`.1081>1082> Подавляет `impl_prototype`, записывает `impl_definition` в `block`, записывает `docstring_prototype`, `methoddef_define` и `parser_prototype` в `two-pass`, а всё остальное записывает в `buffer`.1083>1084> **`partial-buffer`**1085>1086> Аналогичен предустановке `buffer`, но больше вещей записывает в `block`, а действительно большие фрагменты сгенерированного кода – только в `buffer`. Это полностью избегает проблемы «определение до использования» предустановки `buffer`, ценой немного большего содержимого в выводе блока. Сбрасывайте буфер `buffer` ближе к концу, как и при использовании предустановки `buffer`.1087>1088> Подавляет `impl_prototype`, записывает `docstring_definition` и `parser_definition` в `buffer`, а всё остальное записывает в `block`.10891090Третья новая директива – `destination`:10911092```python1093destination <name> <command> [...]1094```10951096Это выполняет операцию над назначением с именем `name`.10971098Существует две определённые подкоманды: `new` и `clear`.10991100Подкоманда `new` работает следующим образом:11011102```python1103destination <name> new <type>1104```11051106Это создаёт новое назначение с именем `<name>` и типом `<type>`.11071108Существует пять типов назначений:11091110> **`suppress`**1111>1112> Отбрасывает текст.1113>1114> **`block`**1115>1116> Записывает текст в текущий блок. Именно так Clinic работал изначально.1117>1118> **`buffer`**1119>1120> Простой текстовый буфер, как встроенное назначение «buffer» выше.1121>1122> **`file`**1123>1124> Текстовый файл. Назначение «файл» принимает дополнительный аргумент – шаблон для построения имени файла, например:1125>1126> > назначение \<имя\> new \<тип\> \<шаблон\_файла\>1127>1128> Внутри шаблона можно использовать три строки, которые будут заменены на части имени файла:1129>1130> > **{path}**1131> >1132> > Полный путь к файлу, включая каталог и полное имя файла.1133> >1134> > **{dirname}**1135> >1136> > Имя каталога, в котором находится файл.1137> >1138> > **{basename}**1139> >1140> > Просто имя файла, без каталога.1141> >1142> > **{basename\_root}**1143> >1144> > Базовое имя без расширения (всё до последней точки, но не включая её).1145> >1146> > **{basename\_extension}**1147> >1148> > Последняя точка и всё после неё. Если в базовом имени нет точки, будет пустая строка.1149>1150> Если в имени файла нет точек, {basename} и {filename} совпадают, а {extension} пуст. «{basename}{extension}» всегда совпадает с «{filename}».1151>1152> **`двухпроходный`**1153>1154> Буфер двухпроходной обработки, как встроенное назначение «two-pass» выше.11551156Подкоманда `clear` работает следующим образом:11571158```python1159destination <name> clear1160```11611162Она удаляет весь накопленный текст до этой точки в назначении. (Не знаю, для чего это может понадобиться, но подумал, что может быть полезно для экспериментов.)11631164Четвёртая новая директива – `set`:11651166```python1167set line_prefix "string"1168set line_suffix "string"1169```11701171Директива `set` позволяет задать две внутренние переменные в Clinic. `line_prefix` – это строка, которая будет добавляться перед каждой строкой вывода Clinic; `line_suffix` – это строка, которая будет добавляться после каждой строки вывода Clinic.11721173Обе поддерживают две строки формата:11741175> **`{block comment start}`**1176>1177> Превращается в строку1178>1179> `/*`1180>1181> , последовательность символов начала комментария для C-файлов.1182>1183> **`{block comment end}`**1184>1185> Превращается в строку1186>1187> `*/`1188>1189> , последовательность символов конца комментария для C-файлов.11901191Последняя новая директива не предназначена для прямого использования и называется `preserve`:11921193```python1194preserve1195```11961197Это указывает Clinic, что текущее содержимое вывода должно быть сохранено без изменений. Это используется внутри Clinic при сбросе вывода в файлы `file`; обёртывание вывода в блок Clinic позволяет Clinic использовать существующую функциональность контрольных сумм, чтобы гарантировать, что файл не был изменён вручную до его перезаписи.11981199### Трюк с #ifdef12001201При преобразовании функции, которая доступна не на всех платформах, можно воспользоваться трюком, чтобы немного упростить жизнь. Существующий код, вероятно, выглядит так:12021203```python1204#ifdef HAVE_FUNCTIONNAME1205static module_functionname(...)1206{1207...1208}1209#endif /* HAVE_FUNCTIONNAME */1210```12111212И затем в структуре `PyMethodDef` в нижней части существующего кода будет:12131214```python1215#ifdef HAVE_FUNCTIONNAME1216{'functionname', ... },1217#endif /* HAVE_FUNCTIONNAME */1218```12191220В этом сценарии тело вашей impl-функции следует заключить внутрь `#ifdef`, вот так:12211222```python1223#ifdef HAVE_FUNCTIONNAME1224/*[clinic input]1225module.functionname1226...1227[clinic start generated code]*/1228static module_functionname(...)1229{1230...1231}1232#endif /* HAVE_FUNCTIONNAME */1233```12341235Затем удалите эти три строки из структуры `PyMethodDef`, заменив их на макрос, сгенерированный Argument Clinic:12361237```python1238MODULE_FUNCTIONNAME_METHODDEF1239```12401241(Вы можете найти реальное имя этого макроса в сгенерированном коде. Или можете вычислить его самостоятельно: это имя вашей функции, как оно определено в первой строке вашего блока, но с заменой точек на подчёркивания, в верхнем регистре и с `"_METHODDEF"`, добавленным в конец.)12421243Возможно, вы задаётесь вопросом: что если `HAVE_FUNCTIONNAME` не определён? Макрос `MODULE_FUNCTIONNAME_METHODDEF` тоже не будет определён!12441245Вот где Argument Clinic проявляет настоящую смекалку. Он фактически обнаруживает, что блок Argument Clinic может быть деактивирован с помощью `#ifdef`. Когда это происходит, он генерирует небольшой дополнительный код, который выглядит так:12461247```python1248#ifndef MODULE_FUNCTIONNAME_METHODDEF1249 #define MODULE_FUNCTIONNAME_METHODDEF1250#endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */1251```12521253Это означает, что макрос всегда работает. Если функция определена, это превращается в правильную структуру, включая завершающую запятую. Если функция не определена, это превращается в ничто.12541255Однако это вызывает одну деликатную проблему: куда Argument Clinic должен поместить этот дополнительный код при использовании предустановки вывода "block"? Он не может находиться в выходном блоке, потому что тот может быть деактивирован с помощью `#ifdef`. (В этом вся суть!)12561257В этой ситуации Argument Clinic записывает дополнительный код в назначение "buffer". Это может означать, что Argument Clinic выдаст предупреждение:12581259```python1260Warning in file "Modules/posixmodule.c" on line 12357:1261Destination buffer 'buffer' not empty at end of file, emptying.1262```12631264Когда это происходит, просто откройте файл, найдите блок `dump buffer`, который Argument Clinic добавил в ваш файл (он будет в самом низу), затем переместите его выше структуры `PyMethodDef`, где используется этот макрос.12651266### Использование Argument Clinic в файлах Python12671268На самом деле Argument Clinic можно использовать для предварительной обработки файлов Python. Конечно, использовать блоки Argument Clinic нет смысла, так как интерпретатор Python не поймёт вывод. Но использование Argument Clinic для выполнения блоков Python позволяет использовать Python в качестве препроцессора для Python!12691270Поскольку комментарии в Python отличаются от комментариев в C, блоки Argument Clinic, встроенные в файлы Python, выглядят немного иначе. Они выглядят так:12711272```python1273#/*[python input]1274#print("def foo(): pass")1275#[python start generated code]*/1276def foo(): pass1277#/*[python checksum:...]*/1278```1279