Документация Python неофициальный перевод

clinic.md

1264 строк · 103.9 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.5/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 эта функция больше не должна выполнять собственный разбор аргументов – сгенерированный 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```console37$ python3 Tools/clinic/clinic.py foo.c38```3940Argument Clinic будет сканировать файл в поисках строк, которые выглядят точно так же:4142```text43/*[clinic input]44```4546Когда он находит такую строку, он читает всё до строки, которая выглядит точно так же:4748```text49[clinic start generated code]*/50```5152Всё, что находится между этими двумя строками, является входными данными для Argument Clinic. Все эти строки, включая начальную и конечную строки комментариев, вместе называются «блоком» Argument Clinic.5354Когда Argument Clinic анализирует один из таких блоков, он генерирует выходные данные. Эти выходные данные перезаписываются в C-файл сразу после блока, за которыми следует комментарий с контрольной суммой. Блок Argument Clinic теперь выглядит так:5556```text57/*[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.5/c-api/arg.html#c.PyArg_ParseTuple), либо [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), и ещё не была преобразована для работы с Argument Clinic. В своём примере я использую `_pickle.Pickler.dump()`.852. Если вызов функции `PyArg_Parse` использует один из следующих форматных юнитов:8687   ```text88   O&89   O!90   es91   es#92   et93   et#94   ```9596   или если в ней несколько вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple), следует выбрать другую функцию. Argument Clinic *поддерживает* все эти сценарии. Но это продвинутые темы – для первой функции лучше сделать что-то попроще.9798   Кроме того, если функция содержит несколько вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) или [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) с разными типами для одного и того же аргумента, или если для разбора аргументов используются не функции PyArg\_Parse, то, вероятно, она не подходит для преобразования в Argument Clinic. Argument Clinic не поддерживает обобщённые функции или полиморфные параметры.993. Добавьте следующий шаблонный код над функцией, создав наш блок:100101   ```c102   /*[clinic input]103   [clinic start generated code]*/104   ```1054. Вырежьте строку документации и вставьте её между строками `[clinic]`, убрав всё, что делает её правильно оформленной строкой C. Когда закончите, у вас должен остаться только текст, выровненный по левому краю, без строк длиннее 80 символов. (Argument Clinic сохранит отступы внутри строки документации.)106107   Если старая строка документации содержала первую строку, похожую на сигнатуру функции, удалите её. (Она больше не нужна – когда вы в будущем будете использовать `help()` для встроенной функции, первая строка будет сгенерирована автоматически на основе сигнатуры функции.)108109   Пример:110111   ```c112   /*[clinic input]113   Записывает сериализованное представление obj в открытый файл.114   [clinic start generated code]*/115   ```1165. Если в строке документации нет строки «краткого описания», Argument Clinic выдаст предупреждение. Поэтому убедитесь, что она есть. Строка краткого описания должна представлять собой абзац из одной строки длиной до 80 символов в начале строки документации.117118   (В нашем примере строка документации состоит только из строки краткого описания, поэтому образец кода не нужно менять на этом шаге.)1196. Над строкой документации укажите имя функции, затем пустую строку. Это должно быть Python-имя функции, полный путь с точками – начиная с имени модуля, включая все подмодули, и если функция является методом класса, то также имя класса.120121   Пример:122123   ```c124   /*[clinic input]125   _pickle.Pickler.dump126127   Записывает сериализованное представление obj в открытый файл.128   [clinic start generated code]*/129   ```1307. Если этот модуль или класс впервые используются с Argument Clinic в данном C-файле, необходимо объявить модуль и/или класс. Правила гигиены Argument Clinic рекомендуют объявлять их в отдельном блоке где-нибудь ближе к началу C-файла, так же как файлы включений и статические переменные размещаются в начале. (В нашем примере мы просто покажем два блока рядом.)131132   Имя класса и модуля должно совпадать с тем, которое видит Python. Проверьте имя, определённое в [`PyModuleDef`](https://python-all.ru/3.5/c-api/module.html#c.PyModuleDef) или [`PyTypeObject`](https://python-all.ru/3.5/c-api/type.html#c.PyTypeObject), в зависимости от ситуации.133134   При объявлении класса необходимо также указать два аспекта его типа в C: объявление типа для указателя на экземпляр этого класса и указатель на [`PyTypeObject`](https://python-all.ru/3.5/c-api/type.html#c.PyTypeObject) для этого класса.135136   Пример:137138   ```c139   /*[clinic input]140   module _pickle141   class _pickle.Pickler "PicklerObject *" "&Pickler_Type"142   [clinic start generated code]*/143144   /*[clinic input]145   _pickle.Pickler.dump146147   Записывает сериализованное представление obj в открытый файл.148   [clinic start generated code]*/149   ```1508. Объявите каждый параметр функции. Каждый параметр должен быть на отдельной строке. Все строки параметров должны быть с отступом относительно имени функции и строки документации.151152   Общий вид этих строк параметров следующий:153154   ```c155   name_of_parameter: converter156   ```157158   Если у параметра есть значение по умолчанию, добавьте его после конвертера:159160   ```c161   name_of_parameter: converter = default_value162   ```163164   Поддержка значений по умолчанию в Argument Clinic довольно сложная; обратитесь к [разделу ниже о значениях по умолчанию](https://python-all.ru/3.5/howto/clinic.html#default-values) для получения дополнительной информации.165166   Добавьте пустую строку под параметрами.167168   Что такое «конвертер»? Он определяет как тип переменной, используемый в C, так и метод преобразования значения Python в значение C во время выполнения. Сейчас вы будете использовать так называемый «унаследованный конвертер» – удобный синтаксис, предназначенный для упрощения переноса старого кода в Argument Clinic.169170   Для каждого параметра скопируйте «форматный юнит» этого параметра из аргумента формата `PyArg_Parse()` и укажите *его* в качестве конвертера, в виде строки в кавычках. («Форматный юнит» – это официальное название подстроки из одного-трёх символов параметра `format`, которая сообщает функции разбора аргументов тип переменной и способ её преобразования. Подробнее о форматных юнитах см. [«Разбор аргументов и сборка значений»](https://python-all.ru/3.5/c-api/arg.html#arg-parsing).)171172   Для многозначных форматных юнитов, таких как `z#`, используйте всю строку из двух или трёх символов.173174   Пример:175176   ```c177    /*[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   Записывает сериализованное представление obj в открытый файл.188   [clinic start generated code]*/189   ```1909. Если в строке формата вашей функции есть `|`, что означает, что некоторые параметры имеют значения по умолчанию, можете игнорировать его. Argument Clinic определяет, какие параметры необязательны, на основе наличия значений по умолчанию.191192   Если в строке формата вашей функции есть `$`, что означает, что она принимает только именованные аргументы, укажите `*` на отдельной строке перед первым именованным аргументом, с отступом, как у строк параметров.193194`_pickle.Pickler.dump` нет ни того, ни другого, поэтому наш образец не меняется.)19510. Если существующая C-функция вызывает [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) (в отличие от [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords)), то все её аргументы являются только позиционными.196197    Чтобы пометить все параметры как только позиционные в Argument Clinic, добавьте `/` на отдельной строке после последнего параметра, с отступом, как у строк параметров.198199    Сейчас это работает по принципу «всё или ничего»: либо все параметры только позиционные, либо ни один. (В будущем Argument Clinic может ослабить это ограничение.)200201    Пример:202203    ```c204    /*[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    Записывает сериализованное представление obj в открытый файл.216    [clinic start generated code]*/217    ```21811. Полезно писать докстринг на каждый параметр. Но такие докстринги необязательны – при желании этот шаг можно пропустить.219220    Вот как добавить докстринг для параметра. Первая строка этого докстринга должна иметь отступ больше, чем определение параметра. Левый отступ этой первой строки задаёт левый отступ для всего докстринга параметра; весь написанный текст будет сдвинут влево на эту величину. Можно писать сколько угодно текста, в том числе на нескольких строках.221222    Пример:223224    ```c225    /*[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            Объект для сериализации.235        /236237    Записывает сериализованное представление obj в открытый файл.238    [clinic start generated code]*/239    ```24012. Сохраните и закройте файл, затем запустите на нём `Tools/clinic/clinic.py`. Если повезёт, всё сработало – теперь у вашего блока есть вывод, а также сгенерирован файл `.c.h`! Снова откройте файл в текстовом редакторе, чтобы увидеть:241242    ```c243    /*[clinic input]244    _pickle.Pickler.dump245246        obj: 'O'247            Объект для сериализации.248        /249250    Записывает сериализованное представление obj в открытый файл.251    [clinic start generated code]*/252253    static PyObject *254    _pickle_Pickler_dump(PicklerObject *self, PyObject *obj)255    /*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/256    ```257258    Очевидно, если Argument Clinic не выдал никакого вывода, значит, он нашёл ошибку во входных данных. Исправляйте ошибки и повторяйте запуск, пока Argument Clinic не обработает файл без нареканий.259260    Для удобства чтения большая часть связующего кода сгенерирована в файл `.c.h`. Этот файл нужно включить в исходный файл `.c`, как правило, сразу после блока модуля clinic:261262    ```c263    #include "clinic/_pickle.c.h"264    ```26513. Перепроверьте, что сгенерированный Argument Clinic код разбора аргументов в основном совпадает с существующим кодом.266267    Во-первых, убедитесь, что в обоих местах используется одна и та же функция разбора аргументов. Существующий код должен вызывать либо [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple), либо [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords); добейтесь, чтобы код, сгенерированный Argument Clinic, вызывал *ту же самую* функцию.268269    Во-вторых, строка формата, передаваемая в [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) или [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), должна быть *в точности* такой же, как написанная вручную в существующей функции, вплоть до двоеточия или точки с запятой.270271    (Argument Clinic всегда генерирует строки формата с `:`, за которым следует имя функции. Если строка формата существующего кода заканчивается на `;` для вывода справки по использованию, это изменение безвредно – не беспокойтесь об этом.)272273    В-третьих, для параметров, единицы формата которых требуют двух аргументов (например, переменная длины, строка кодировки или указатель на функцию преобразования), убедитесь, что второй аргумент *в точности* одинаков в обоих вызовах.274275    В-четвёртых, внутри выходной части блока вы найдёте макрос препроцессора, определяющий соответствующую статическую структуру [`PyMethodDef`](https://python-all.ru/3.5/c-api/structures.html#c.PyMethodDef) для этой встроенной функции:276277    ```c278    #define __PICKLE_PICKLER_DUMP_METHODDEF    \279    {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},280    ```281282    Эта статическая структура должна быть *в точности* такой же, как существующая статическая структура [`PyMethodDef`](https://python-all.ru/3.5/c-api/structures.html#c.PyMethodDef) для этой встроенной функции.283284    Если какой-либо из этих элементов *хоть в чём-то* отличается, измените спецификацию функции в Argument Clinic и повторно запустите `Tools/clinic/clinic.py`, пока они не *станут* одинаковыми.28514. Обратите внимание, что последняя строка вывода – это объявление вашей функции «impl». Сюда помещается реализация встроенной функции. Удалите существующий прототип изменяемой функции, но оставьте открывающую фигурную скобку. Затем удалите код разбора её аргументов и объявления всех переменных, в которые они помещались. Обратите внимание, что аргументы Python теперь стали аргументами этой функции impl; если в реализации использовались другие имена для этих переменных, исправьте это.286287    Повторим ещё раз, просто потому что это необычно. Теперь ваш код должен выглядеть так:288289    ```c290    static return_type291    your_function_impl(...)292    /*[clinic end generated code: checksum=...]*/293    {294    ...295    ```296297    Argument Clinic сгенерировал строку контрольной суммы и прототип функции прямо над ней. Вам нужно написать открывающую (и закрывающую) фигурные скобки для функции и реализацию внутри.298299    Пример:300301    ```c302    /*[clinic input]303    module _pickle304    class _pickle.Pickler "PicklerObject *" "&Pickler_Type"305    [clinic start generated code]*/306    /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/307308    /*[clinic input]309    _pickle.Pickler.dump310311        obj: 'O'312            Объект для сериализации.313        /314315    Записывает сериализованное представление obj в открытый файл.316    [clinic start generated code]*/317318    PyDoc_STRVAR(__pickle_Pickler_dump__doc__,319    "Write a pickled representation of obj to the open file.\n"320    "\n"321    ...322    static PyObject *323    _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)324    /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/325    {326        /* Проверяет, был ли Pickler инициализирован корректно (issue3664).327           Разработчики часто забывают вызывать __init__() в своих подклассах, что328           привело бы к segfault без этой проверки. */329        if (self->write == NULL) {330            PyErr_Format(PicklingError,331                         "Pickler.__init__() was not called by %s.__init__()",332                         Py_TYPE(self)->tp_name);333            return NULL;334        }335336        if (_Pickler_ClearBuffer(self) < 0)337            return NULL;338339        ...340    ```34115. Помните макрос со структурой [`PyMethodDef`](https://python-all.ru/3.5/c-api/structures.html#c.PyMethodDef) для этой функции? Найдите существующую структуру [`PyMethodDef`](https://python-all.ru/3.5/c-api/structures.html#c.PyMethodDef) для этой функции и замените её ссылкой на макрос. (Если встроенная функция находится на уровне модуля, это, скорее всего, будет почти в конце файла; если это метод класса, то, вероятно, ниже, но относительно рядом с реализацией.)342343    Обратите внимание, что тело макроса содержит завершающую запятую. Поэтому при замене существующей статической структуры [`PyMethodDef`](https://python-all.ru/3.5/c-api/structures.html#c.PyMethodDef) на макрос *не* добавляйте запятую в конце.344345    Пример:346347    ```c348    static struct PyMethodDef Pickler_methods[] = {349        __PICKLE_PICKLER_DUMP_METHODDEF350        __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF351        {NULL, NULL}                /* сторожевое значение */352    };353    ```35416. Скомпилируйте, затем запустите соответствующие части набора регрессионных тестов. Это изменение не должно приводить к появлению новых предупреждений или ошибок во время компиляции, и не должно вызывать видимых извне изменений в поведении Python.355356    Ну, за одним исключением: `inspect.signature()`, запущенная на вашей функции, теперь должна выдавать корректную сигнатуру!357358    Поздравляем, вы портировали свою первую функцию для работы с Argument Clinic!359360## Продвинутые темы361362Теперь, когда у вас есть некоторый опыт работы с Argument Clinic, пора перейти к продвинутым темам.363364### Символические значения по умолчанию365366Значение по умолчанию для параметра не может быть произвольным выражением. В настоящее время явно поддерживаются следующие:367368- Числовые константы (целые и с плавающей запятой)369- Строковые константы370- `True`, `False` и `None`371- Простые символические константы, такие как `sys.maxsize`, должны начинаться с имени модуля372373Если вам интересно, это реализовано в `from_builtin()` в `Lib/inspect.py`.374375(В будущем это может потребовать ещё большей доработки, чтобы разрешить полноценные выражения вроде `CONSTANT - 1`.)376377### Переименование C-функций и переменных, сгенерированных Argument Clinic378379Argument Clinic автоматически присваивает имена сгенерированным функциям. Иногда это может вызывать проблему, если сгенерированное имя совпадает с именем существующей C-функции. Есть простое решение: переопределите имена, используемые для C-функций. Просто добавьте ключевое слово `"as"` в строку объявления функции, а затем укажите желаемое имя функции. Argument Clinic будет использовать это имя для базовой (сгенерированной) функции, после чего добавит `"_impl"` в конец и будет использовать его для имени функции реализации.380381Например, если бы мы захотели переименовать C-функции, сгенерированные для `pickle.Pickler.dump`, это выглядело бы так:382383```c384/*[clinic input]385pickle.Pickler.dump as pickler_dumper386387...388```389390Теперь базовая функция будет называться `pickler_dumper()`, а функция реализации – `pickler_dumper_impl()`.391392Аналогично, может возникнуть ситуация, когда нужно дать параметру конкретное имя Python, но это имя может быть неудобным в C. Argument Clinic позволяет задать параметру разные имена в Python и C, используя тот же синтаксис `"as"`:393394```c395/*[clinic input]396pickle.Pickler.dump397398    obj: object399    file as file_obj: object400    протокол: object = NULL401    *402    fix_imports: bool = True403```404405Здесь имя, используемое в Python (в сигнатуре и массиве `keywords`), будет `file`, а C-переменная будет называться `file_obj`.406407Это можно использовать и для переименования параметра `self`!408409### Преобразование функций, использующих PyArg\_UnpackTuple410411Чтобы преобразовать функцию, которая разбирает свои аргументы с помощью [`PyArg_UnpackTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_UnpackTuple), просто перечислите все аргументы, указав каждый как `object`. Можно указать аргумент `type` для приведения типа по необходимости. Все аргументы должны быть помечены как только позиционные (добавьте `/` на отдельной строке после последнего аргумента).412413Сейчас сгенерированный код будет использовать [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple), но это скоро изменится.414415### Опциональные группы416417Некоторые устаревшие функции используют хитрый подход к разбору своих аргументов: они подсчитывают количество позиционных аргументов, затем с помощью оператора `switch` вызывают один из нескольких разных вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) в зависимости от количества позиционных аргументов. (Эти функции не могут принимать только ключевые аргументы.) Этот подход использовался для имитации опциональных аргументов до того, как был создан [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords).418419Хотя функции, использующие этот подход, часто можно преобразовать для использования [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords), опциональных аргументов и значений по умолчанию, это не всегда возможно. Некоторые из этих устаревших функций имеют поведение, которое [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) напрямую не поддерживает. Самый очевидный пример – встроенная функция `range()`, у которой есть опциональный аргумент с *левой* стороны от обязательного аргумента! Другой пример – `curses.window.addch()`, у которой есть группа из двух аргументов, которые всегда должны указываться вместе. (Аргументы называются `x` и `y`; если вы вызываете функцию, передавая `x`, вы также должны передать `y` – а если вы не передаёте `x`, то нельзя передавать и `y`.)420421В любом случае, цель Argument Clinic – поддерживать разбор аргументов для всех существующих встроенных функций CPython без изменения их семантики. Поэтому Argument Clinic поддерживает этот альтернативный подход к разбору, используя так называемые *опциональные группы*. Опциональные группы – это группы аргументов, которые должны передаваться вместе. Они могут находиться слева или справа от обязательных аргументов. Их можно использовать *только* с только позиционными параметрами.422423> **Примечание**424>425> Опциональные группы *предназначены только* для использования при преобразовании функций, которые выполняют несколько вызовов [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple)! Функции, использующие *любой другой* подход для разбора аргументов, *почти никогда* не должны преобразовываться в Argument Clinic с использованием опциональных групп. Функции, использующие опциональные группы, в настоящее время не могут иметь точные сигнатуры в Python, потому что Python просто не понимает эту концепцию. Пожалуйста, по возможности избегайте использования опциональных групп.426427Чтобы указать опциональную группу, добавьте `[` на отдельной строке перед параметрами, которые нужно сгруппировать, и `]` на отдельной строке после этих параметров. В качестве примера, вот как `curses.window.addch` использует опциональные группы, чтобы сделать первые два параметра и последний параметр опциональными:428429```c430/*[clinic input]431432curses.window.addch433434    [435    x: int436      Координата X.437    y: int438      Координата Y.439    ]440441    ch: object442      Добавляемый символ.443444    [445    attr: long446      Атрибуты для символа.447    ]448    /449450...451```452453Примечания:454455- Для каждой опциональной группы в функцию реализации будет передан один дополнительный параметр, представляющий группу. Параметр будет иметь тип int и имя `group_{direction}_{number}`, где `{direction}` – это либо `right`, либо `left` в зависимости от того, находится ли группа до или после обязательных параметров, а `{number}` – монотонно возрастающее число (начиная с 1), указывающее, насколько далеко группа находится от обязательных параметров. Когда вызывается функция реализации, этот параметр будет установлен в ноль, если группа не использовалась, и в ненулевое значение, если группа использовалась. (Под использованием или неиспользованием я имею в виду, получили ли параметры аргументы в этом вызове.)456- Если обязательных аргументов нет, опциональные группы будут вести себя так, как если бы они находились справа от обязательных аргументов.457- В случае неоднозначности код разбора аргументов отдаёт предпочтение параметрам слева (перед обязательными параметрами).458- Опциональные группы могут содержать только позиционные параметры.459- Опциональные группы *предназначены только* для устаревшего кода. Пожалуйста, не используйте опциональные группы в новом коде.460461### Использование настоящих конвертеров Argument Clinic вместо «устаревших конвертеров»462463Чтобы сэкономить время и минимизировать объём знаний, необходимых для первого переноса кода на Argument Clinic, в приведённом выше пошаговом руководстве рекомендуется использовать «устаревшие конвертеры». «Устаревшие конвертеры» – это удобство, созданное специально для упрощения переноса существующего кода на Argument Clinic. И, чтобы было понятно, их использование допустимо при переносе кода для Python 3.4.464465Однако в долгосрочной перспективе, вероятно, желательно, чтобы все наши блоки использовали настоящий синтаксис конвертеров Argument Clinic. Почему? Несколько причин:466467- Правильные конвертеры гораздо легче читать, и их назначение понятнее.468- Существуют некоторые форматные единицы, которые не поддерживаются в качестве «устаревших конвертеров», поскольку они требуют аргументов, а синтаксис устаревших конвертеров не поддерживает указание аргументов.469- В будущем у нас может появиться новая библиотека для разбора аргументов, которая не будет ограничена тем, что поддерживает [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple); эта гибкость не будет доступна параметрам, использующим устаревшие конвертеры.470471Поэтому, если вы не против небольшого дополнительного усилия, пожалуйста, используйте обычные конвертеры вместо устаревших.472473В двух словах, синтаксис (неустаревших) конвертеров Argument Clinic выглядит как вызов функции Python. Однако, если у функции нет явных аргументов (все функции принимают свои значения по умолчанию), скобки можно опустить. Таким образом, `bool` и `bool()` – это абсолютно одинаковые конвертеры.474475Все аргументы конвертеров Argument Clinic являются только ключевыми (keyword-only). Все конвертеры Argument Clinic принимают следующие аргументы:476477> **`c_default`**478>479> Значение по умолчанию для этого параметра при определении в C. А именно, это будет инициализатор переменной, объявленной в «функции разбора» (parse function). См.480>481> [раздел о значениях по умолчанию](https://python-all.ru/3.5/howto/clinic.html#default-values)482>483> , чтобы узнать, как это использовать. Указывается в виде строки.484>485> **`annotation`**486>487> Значение аннотации для этого параметра. В настоящее время не поддерживается, поскольку PEP 8 требует, чтобы библиотека Python не использовала аннотации.488489Кроме того, некоторые конвертеры принимают дополнительные аргументы. Вот список этих аргументов с их значениями:490491> **`accept`**492>493> Набор типов Python (и, возможно, псевдотипов); это ограничивает допустимые аргументы Python значениями этих типов. (Это не универсальный механизм; как правило, он поддерживает только конкретные списки типов, как показано в таблице устаревших конвертеров.)494>495> Чтобы принимать `None`, добавьте `NoneType` в этот набор.496>497> **`bitwise`**498>499> Поддерживается только для беззнаковых целых чисел. Собственное целочисленное значение этого аргумента Python будет записано в параметр без какой-либо проверки диапазона, даже для отрицательных значений.500>501> **`converter`**502>503> Поддерживается только конвертером504>505> `object`506>507> . Указывает имя508>509> [C-«функции-конвертера»](https://python-all.ru/3.5/c-api/arg.html#o-ampersand)510>511> , используемой для преобразования этого объекта в собственный тип.512>513> **`encoding`**514>515> Поддерживается только для строк. Указывает кодировку, используемую при преобразовании этой строки из значения Python str (Unicode) в значение C516>517> `char *`518>519> .520>521> **`subclass_of`**522>523> Поддерживается только для конвертера524>525> `object`526>527> . Требует, чтобы значение Python было подклассом типа Python, как это выражается в C.528>529> **`type`**530>531> Поддерживается только для конвертеров532>533> `object`534>535> и536>537> `self`538>539> . Указывает тип C, который будет использоваться для объявления переменной. Значение по умолчанию:540>541> `"PyObject *"`542>543> .544>545> **`zeroes`**546>547> Поддерживается только для строк. Если true, внутри значения допускаются встроенные NUL-байты (548>549> `'\\0'`550>551> ). Длина строки будет передана в функцию impl сразу после строкового параметра как параметр с именем552>553> `<parameter_name>_length`554>555> .556557Пожалуйста, учтите: не любая комбинация аргументов будет работать. Обычно эти аргументы реализуются конкретными `PyArg_ParseTuple` *форматными единицами* с определённым поведением. Например, в настоящее время нельзя вызвать `unsigned_short`, не указав также `bitwise=True`. Хотя вполне разумно предположить, что это сработает, такая семантика не соответствует ни одной существующей форматной единице. Поэтому Argument Clinic это не поддерживает (или, по крайней мере, пока нет).558559Ниже приведена таблица, показывающая соответствие устаревших конвертеров настоящим конвертерам Argument Clinic. Слева – устаревший конвертер, справа – текст, на который его следует заменить.560561| `'B'` | `unsigned_char(bitwise=True)` |562| --- | --- |563| `'b'` | `unsigned_char` |564| `'c'` | `char` |565| `'C'` | `int(accept={str})` |566| `'d'` | `double` |567| `'D'` | `Py_complex` |568| `'es'` | `str(encoding='name_of_encoding')` |569| `'es#'` | `str(encoding='name_of_encoding', zeroes=True)` |570| `'et'` | `str(encoding='name_of_encoding', accept={bytes, bytearray, str})` |571| `'et#'` | `str(encoding='name_of_encoding', accept={bytes, bytearray, str}, zeroes=True)` |572| `'f'` | `float` |573| `'h'` | `short` |574| `'H'` | `unsigned_short(bitwise=True)` |575| `'i'` | `int` |576| `'I'` | `unsigned_int(bitwise=True)` |577| `'k'` | `unsigned_long(bitwise=True)` |578| `'K'` | `unsigned_PY_LONG_LONG(bitwise=True)` |579| `'l'` | `long` |580| `'L'` | `PY_LONG_LONG` |581| `'n'` | `Py_ssize_t` |582| `'O'` | `object` |583| `'O!'` | `object(subclass_of='&PySomething_Type')` |584| `'O&'` | `object(converter='name_of_c_function')` |585| `'p'` | `bool` |586| `'S'` | `PyBytesObject` |587| `'s'` | `str` |588| `'s#'` | `str(zeroes=True)` |589| `'s*'` | `Py_buffer(accept={buffer, str})` |590| `'U'` | `unicode` |591| `'u'` | `Py_UNICODE` |592| `'u#'` | `Py_UNICODE(zeroes=True)` |593| `'w*'` | `Py_buffer(accept={rwbuffer})` |594| `'Y'` | `PyByteArrayObject` |595| `'y'` | `str(accept={bytes})` |596| `'y#'` | `str(accept={robuffer}, zeroes=True)` |597| `'y*'` | `Py_buffer` |598| `'Z'` | `Py_UNICODE(accept={str, NoneType})` |599| `'Z#'` | `Py_UNICODE(accept={str, NoneType}, zeroes=True)` |600| `'z'` | `str(accept={str, NoneType})` |601| `'z#'` | `str(accept={str, NoneType}, zeroes=True)` |602| `'z*'` | `Py_buffer(accept={buffer, str, NoneType})` |603604В качестве примера – наш образец `pickle.Pickler.dump` с использованием правильного конвертера:605606```c607/*[clinic input]608pickle.Pickler.dump609610    obj: object611        Объект для сериализации.612    /613614Записывает сериализованное представление obj в открытый файл.615[clinic start generated code]*/616```617618Argument Clinic покажет все доступные конвертеры. Для каждого конвертера будут показаны все принимаемые параметры и значения по умолчанию. Просто запустите `Tools/clinic/clinic.py --converters`, чтобы увидеть полный список.619620### Py\_buffer621622При использовании конвертера `Py_buffer` (или устаревших конвертеров `'s*'`, `'w*'`, `'*y'`, `'z*'`) *не* вызывайте [`PyBuffer_Release()`](https://python-all.ru/3.5/c-api/buffer.html#c.PyBuffer_Release) для предоставленного буфера. Argument Clinic генерирует код, который делает это за вас (в функции разбора).623624### Продвинутые конвертеры625626Помните те форматные единицы, которые вы пропустили в первый раз, потому что они были сложными? Вот как с ними работать.627628Секрет в том, что все эти форматные единицы принимают аргументы – либо функции преобразования, либо типы, либо строки с указанием кодировки. (Но «устаревшие конвертеры» не поддерживают аргументы. Поэтому мы пропустили их для вашей первой функции.) Аргумент, который вы указывали для форматной единицы, теперь является аргументом конвертера; этот аргумент может быть `converter` (для `O&`), `subclass_of` (для `O!`) или `encoding` (для всех форматных единиц, начинающихся с `e`).629630При использовании `subclass_of` вы также можете использовать другой пользовательский аргумент для `object()`: `type`, который позволяет задать фактический тип, используемый для параметра. Например, если вы хотите гарантировать, что объект является подклассом `PyUnicode_Type`, вероятно, стоит использовать конвертер `object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')`.631632Одна из возможных проблем при использовании Argument Clinic: он лишает некоторой гибкости форматные единицы, начинающиеся с `e`. При написании вызова `PyArg_Parse` вручную можно было теоретически на этапе выполнения решить, какую строку кодировки передать в [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple). Но теперь эта строка должна быть жестко задана на этапе предварительной обработки Argument Clinic. Это ограничение намеренное: оно значительно упростило поддержку этой форматной единицы и может позволить будущие оптимизации. Такое ограничение не кажется необоснованным; сам CPython всегда передает статические жестко заданные строки кодировки для параметров, чьи форматные единицы начинаются с `e`.633634### Значения по умолчанию параметров635636Значения по умолчанию параметров могут быть различными. В простейшем случае это могут быть строковые, целочисленные литералы или литералы с плавающей точкой:637638```c639foo: str = "abc"640bar: int = 123641bat: float = 45.6642```643644Также могут использоваться любые встроенные константы Python:645646```c647yep:  bool = True648nope: bool = False649nada: object = None650```651652Также есть специальная поддержка значения по умолчанию `NULL` и простых выражений, описанная в следующих разделах.653654### Значение по умолчанию `NULL`655656Для строковых параметров и параметров-объектов можно установить их в `None`, чтобы указать отсутствие значения по умолчанию. Однако это означает, что переменная C будет инициализирована значением `Py_None`. Для удобства существует специальное значение `NULL` как раз для этого: с точки зрения Python оно ведёт себя как значение по умолчанию `None`, но переменная C инициализируется значением `NULL`.657658### Выражения, задаваемые как значения по умолчанию659660Значение по умолчанию для параметра может быть не просто литералом. Это может быть целое выражение, использующее математические операторы и обращающееся к атрибутам объектов. Однако эта поддержка не совсем проста из-за некоторых неочевидных семантических нюансов.661662Рассмотрим следующий пример:663664```c665foo: Py_ssize_t = sys.maxsize - 1666```667668`sys.maxsize` может принимать разные значения на разных платформах. Поэтому Argument Clinic не может просто вычислить это выражение локально и жёстко закодировать его в C. Вместо этого он сохраняет значение по умолчанию так, чтобы оно вычислялось во время выполнения, когда пользователь запрашивает сигнатуру функции.669670Какое пространство имён доступно при вычислении выражения? Оно вычисляется в контексте модуля, из которого происходит встроенная функция. Поэтому, если в модуле есть атрибут с именем «`max_widgets`», его можно просто использовать:671672```c673foo: Py_ssize_t = max_widgets674```675676Если символ не найден в текущем модуле, поиск продолжается в `sys.modules`. Именно так, например, можно найти `sys.maxsize`. (Поскольку заранее неизвестно, какие модули пользователь загрузит в интерпретатор, лучше ограничиться модулями, которые предварительно загружает сам Python.)677678Вычисление значений по умолчанию только во время выполнения означает, что Argument Clinic не может вычислить правильное эквивалентное значение C по умолчанию. Поэтому необходимо указать его явно. При использовании выражения нужно также указать эквивалентное выражение на C с помощью параметра `c_default` конвертера:679680```c681foo: Py_ssize_t(c_default="PY_SSIZE_T_MAX - 1") = sys.maxsize - 1682```683684Ещё одно осложнение: Argument Clinic не может заранее узнать, является ли предоставленное выражение корректным. Он разбирает его, чтобы убедиться, что оно выглядит допустимым, но не может *на самом деле* знать. Нужно быть очень осторожным при использовании выражений для указания значений, которые гарантированно будут корректными во время выполнения!685686Наконец, поскольку выражения должны быть представимы как статические значения C, на допустимые выражения накладывается много ограничений. Вот список возможностей Python, которые использовать запрещено:687688- Вызовы функций.689- Условные выражения (`3 if foo else 5`).690- Автоматическая распаковка последовательностей (`*[1, 2, 3]`).691- Генераторы списков, множеств, словарей и выражения-генераторы.692- Литералы кортежей, списков, множеств, словарей.693694### Использование конвертера возвращаемого значения695696По умолчанию функция impl, генерируемая Argument Clinic, возвращает `PyObject *`. Но C-функция часто вычисляет некоторый тип C, а затем преобразует его в `PyObject *` в последний момент. Argument Clinic занимается преобразованием входных данных из типов Python в нативные типы C – почему бы не поручить ему также преобразование возвращаемого значения из нативного типа C в тип Python?697698Именно это и делает «конвертер возвращаемого значения». Он изменяет функцию impl так, чтобы она возвращала некоторый тип C, а затем добавляет код в сгенерированную (не-impl) функцию для преобразования этого значения в соответствующий `PyObject *`.699700Синтаксис конвертеров возвращаемого значения аналогичен синтаксису конвертеров параметров. Конвертер возвращаемого значения указывается как аннотация возврата самой функции. Конвертеры возвращаемого значения ведут себя почти так же, как конвертеры параметров: они принимают аргументы, все аргументы являются только ключевыми, и если ни один из аргументов по умолчанию не изменяется, скобки можно опустить.701702(Если используются и `"as"`, *и* конвертер возвращаемого значения для функции, то `"as"` должен идти перед конвертером возвращаемого значения.)703704При использовании конвертеров возвращаемого значения есть ещё одно осложнение: как указать, что произошла ошибка? Обычно функция возвращает корректный (не `NULL`) указатель при успехе и `NULL` при неудаче. Но если используется целочисленный конвертер возвращаемого значения, все целые числа являются корректными. Как Argument Clinic может обнаружить ошибку? Его решение: каждый конвертер возвращаемого значения неявно ищет специальное значение, указывающее на ошибку. Если возвращается это значение и ошибка была установлена (`PyErr_Occurred()` возвращает истинное значение), то сгенерированный код распространит ошибку. В противном случае он закодирует возвращённое значение как обычно.705706В настоящее время Argument Clinic поддерживает лишь несколько конвертеров возвращаемого значения:707708```text709bool710int711unsigned int712long713unsigned int714size_t715Py_ssize_t716float717double718DecodeFSDefault719```720721Ни один из них не принимает параметров. Для первых трёх возвращается -1, указывающий на ошибку. Для `DecodeFSDefault` возвращаемый тип – `char *`; для указания на ошибку возвращается нулевой указатель (NULL).722723(Существует также экспериментальный конвертер `NoneType`, который позволяет возвращать `Py_None` при успехе или `NULL` при неудаче, без необходимости увеличивать счётчик ссылок на `Py_None`. Не уверен, что он добавляет достаточно ясности, чтобы его стоило использовать.)724725Чтобы увидеть все конвертеры возвращаемого значения, поддерживаемые Argument Clinic, вместе с их параметрами (если они есть), просто запустите `Tools/clinic/clinic.py --converters` для получения полного списка.726727### Клонирование существующих функций728729Если есть несколько функций, которые выглядят похоже, можно воспользоваться функцией «клонирования» в Clinic. При клонировании существующей функции повторно используются:730731- её параметры, включая732733  - их имена,734  - их конвертеры, со всеми параметрами,735  - их значения по умолчанию,736  - их документирующие строки для каждого параметра,737  - их *вид* (являются ли они только позиционными, позиционными или ключевыми, или только ключевыми), и738- её конвертер возвращаемого значения.739740Единственное, что не копируется из исходной функции, – это её документирующая строка; синтаксис позволяет указать новую документирующую строку.741742Вот синтаксис клонирования функции:743744```c745/*[clinic input]746module.class.new_function [as c_basename] = module.class.existing_function747748Докстринг для new_function.749[clinic start generated code]*/750```751752(Функции могут находиться в разных модулях или классах. Я написал `module.class` в примере просто для иллюстрации того, что необходимо использовать полный путь к *обеим* функциям.)753754К сожалению, нет синтаксиса для частичного клонирования функции или клонирования функции с последующим её изменением. Клонирование – это принцип «всё или ничего».755756Также функция, с которой производится клонирование, должна быть ранее определена в текущем файле.757758### Вызов кода Python759760Для остальных продвинутых тем требуется написать код Python, который находится внутри C-файла и изменяет состояние времени выполнения Argument Clinic. Это просто: нужно всего лишь определить блок Python.761762Блок Python использует другие строки-разделители, нежели блок функции Argument Clinic. Выглядит это так:763764```c765/*[python input]766# Сюда помещается код Python.767[python start generated code]*/768```769770Весь код внутри блока Python выполняется в момент его разбора. Весь текст, выводимый в stdout внутри блока, перенаправляется в «вывод» после блока.771772В качестве примера приведён блок Python, который добавляет статическую целочисленную переменную в код C:773774```c775/*[python input]776print('static int __ignored_unused_variable__ = 0;')777[python start generated code]*/778static int __ignored_unused_variable__ = 0;779/*[python checksum:...]*/780```781782### Использование «конвертера self»783784Argument Clinic автоматически добавляет параметр «self» с помощью конвертера по умолчанию. Он автоматически устанавливает `type` этого параметра на «указатель на экземпляр», указанный вами при объявлении типа. Однако вы можете переопределить конвертер Argument Clinic и указать свой собственный. Просто добавьте собственный параметр `self` в качестве первого параметра в блоке и убедитесь, что его конвертер является экземпляром `self_converter` или его подкласса.785786В чём смысл? Это позволяет переопределить тип `self` или задать ему другое имя по умолчанию.787788Как указать пользовательский тип, к которому нужно привести `self`? Если у вас всего одна или две функции с одинаковым типом для `self`, можно напрямую использовать существующий конвертер `self` в Argument Clinic, передав нужный тип в качестве параметра `type`:789790```c791/*[clinic input]792793_pickle.Pickler.dump794795  self: self(type="PicklerObject *")796  obj: object797  /798799Записывает сериализованное представление данного объекта в открытый файл.800[clinic start generated code]*/801```802803С другой стороны, если у вас много функций, использующих один и тот же тип для `self`, лучше создать свой собственный конвертер, унаследовав его от `self_converter`, но переопределив член `type`:804805```c806/*[python input]807class PicklerObject_converter(self_converter):808    type = "PicklerObject *"809[python start generated code]*/810811/*[clinic input]812813_pickle.Pickler.dump814815  self: PicklerObject816  obj: object817  /818819Записывает сериализованное представление данного объекта в открытый файл.820[clinic start generated code]*/821```822823### Создание пользовательского конвертера824825Как мы намекнули в предыдущем разделе… вы можете писать свои собственные конвертеры! Конвертер – это просто класс Python, наследующий от `CConverter`. Основная цель пользовательского конвертера – наличие параметра, использующего единицу формата `O&` – разбор этого параметра означает вызов функции-конвертера [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple).826827Ваш класс конвертера должен называться `*something*_converter`. Если имя соответствует этому соглашению, класс конвертера будет автоматически зарегистрирован в Argument Clinic; его именем станет имя вашего класса без суффикса `_converter`. (Это достигается с помощью метакласса.)828829Не следует наследовать `CConverter.__init__`. Вместо этого нужно написать функцию `converter_init()`. `converter_init()` всегда принимает параметр `self`; после этого все дополнительные параметры *должны* быть только ключевыми. Любые аргументы, переданные конвертеру в Argument Clinic, будут переданы вашей `converter_init()`.830831Существуют некоторые дополнительные члены `CConverter`, которые вы, возможно, захотите указать в вашем подклассе. Вот текущий список:832833**`type`**834835Тип C, используемый для этой переменной.836837`type`838839должен быть строкой Python, задающей тип, например,840841`int`842843. Если это тип указателя, строка типа должна заканчиваться на844845`' *'`846847.848849**`default`**850851Значение по умолчанию для этого параметра в Python, в виде значения Python. Или магическое значение852853`unspecified`854855, если значения по умолчанию нет.856857**`py_default`**858859`default`860861в том виде, в котором он должен отображаться в коде Python, в виде строки. Или862863`None`864865, если значения по умолчанию нет.866867**`c_default`**868869`default`870871в том виде, в котором он должен отображаться в коде C, в виде строки. Или872873`None`874875, если значения по умолчанию нет.876877**`c_ignored_default`**878879Значение по умолчанию, используемое для инициализации переменной C, если значение по умолчанию не задано. Однако отсутствие значения по умолчанию может привести к предупреждению о «неинициализированной переменной». Это может легко произойти при использовании групп опций – хотя корректно написанный код никогда не будет использовать это значение, переменная всё равно передаётся в функцию impl, и компилятор C выдаст предупреждение об «использовании» неинициализированного значения. Это значение всегда должно быть непустой строкой.880881**`converter`**882883Имя функции-преобразователя C в виде строки.884885**`impl_by_reference`**886887Логическое значение. Если истинно, Argument Clinic добавит888889`&`890891перед именем переменной при передаче её в функцию impl.892893**`parse_by_reference`**894895Логическое значение. Если истинно, Argument Clinic добавит896897`&`898899перед именем переменной при передаче её в900901[`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple)902903.904905Вот простейший пример пользовательского преобразователя из `Modules/zlibmodule.c`:906907```c908/*[python input]909910class ssize_t_converter(CConverter):911    type = 'Py_ssize_t'912    converter = 'ssize_t_converter'913914[python start generated code]*/915/*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/916```917918Этот блок добавляет в Argument Clinic преобразователь с именем `ssize_t`. Параметры, объявленные как `ssize_t`, будут объявлены как тип `Py_ssize_t` и будут разобраны форматным элементом `'O&'`, который вызовет функцию-преобразователь `ssize_t_converter`. Переменные `ssize_t` автоматически поддерживают значения по умолчанию.919920Более сложные пользовательские преобразователи могут вставлять собственный код C для обработки инициализации и очистки. Дополнительные примеры пользовательских преобразователей можно найти в исходном коде CPython; выполните grep по файлам C для строки `CConverter`.921922### Написание пользовательского преобразователя возвращаемого значения923924Написание пользовательского преобразователя возвращаемого значения во многом похоже на написание обычного пользовательского преобразователя, только несколько проще, потому что сами преобразователи возвращаемых значений намного проще.925926Преобразователи возвращаемых значений должны быть подклассами `CReturnConverter`. Примеров пользовательских преобразователей возвращаемых значений пока нет, поскольку они ещё не получили широкого распространения. Если вы хотите написать свой собственный преобразователь возвращаемого значения, прочтите `Tools/clinic/clinic.py`, в частности реализацию `CReturnConverter` и всех его подклассов.927928### METH\_O и METH\_NOARGS929930Чтобы преобразовать функцию, использующую `METH_O`, убедитесь, что её единственный аргумент использует преобразователь `object`, и пометьте аргументы как позиционные:931932```c933/*[clinic input]934meth_o_sample935936     argument: object937     /938[clinic start generated code]*/939```940941Чтобы преобразовать функцию, использующую `METH_NOARGS`, просто не указывайте никаких аргументов.942943По-прежнему можно использовать преобразователь self, преобразователь возвращаемого значения и указать аргумент `type` для преобразователя объекта `METH_O`.944945### Функции tp\_new и tp\_init946947Вы можете преобразовывать функции `tp_new` и `tp_init`. Просто назовите их `__new__` или `__init__` соответственно. Примечания:948949- Сгенерированное имя функции для `__new__` не заканчивается на `__new__`, как было бы по умолчанию. Это просто имя класса, преобразованное в корректный идентификатор C.950- Для этих функций не генерируется `PyMethodDef` `#define`.951- Функции `__init__` возвращают `int`, а не `PyObject *`.952- Используйте строку документации как строку документации класса.953- Хотя функции `__new__` и `__init__` всегда должны принимать оба объекта `args` и `kwargs`, при преобразовании вы можете указать для этих функций любую сигнатуру. (Если ваша функция не поддерживает ключевые слова, сгенерированная функция разбора вызовет исключение при их получении.)954955### Изменение и перенаправление вывода Clinic956957Может быть неудобно, когда вывод Clinic перемежается с вашим обычным кодом C, отредактированным вручную. К счастью, Clinic настраивается: можно буферизовать его вывод для печати позже (или раньше!) или записывать вывод в отдельный файл. Также можно добавить префикс или суффикс к каждой строке сгенерированного вывода Clinic.958959Хотя такое изменение вывода Clinic может улучшить читаемость, это может привести к тому, что код Clinic будет использовать типы до их определения, или ваш код будет пытаться использовать сгенерированный Clinic код до его определения. Эти проблемы легко решаются перестановкой объявлений в файле или перемещением места, куда помещается сгенерированный код Clinic. (Именно поэтому поведение Clinic по умолчанию – выводить всё в текущий блок; хотя многие считают, что это затрудняет чтение, это никогда не потребует перестановки кода для исправления проблем использования до определения.)960961Начнём с определения некоторых терминов:962963***поле***964965Поле в данном контексте – это подраздел вывода Clinic. Например, `#define` для структуры `PyMethodDef` является полем, называемым `methoddef_define`. Clinic может выводить семь различных полей для каждого определения функции:966967```c968docstring_prototype969docstring_definition970methoddef_define971impl_prototype972parser_prototype973parser_definition974impl_definition975```976977Все имена имеют вид `"<a>_<b>"`, где `"<a>"` – это представляемый семантический объект (функция разбора, функция impl, строка документации или структура methoddef), а `"<b>"` обозначает, какой тип оператора представляет поле. Имена полей, заканчивающиеся на `"_prototype"`, представляют предварительные объявления этой сущности без фактического тела/данных; имена полей, заканчивающиеся на `"_definition"`, представляют фактическое определение сущности с телом/данными. (`"methoddef"` особое, это единственное, заканчивающееся на `"_define"`, что означает, что это директива препроцессора #define.)978979***назначение***980981Назначение – это место, куда Clinic может записывать вывод. Существует пять встроенных назначений:982983**`block`**984985Назначение по умолчанию: выводится в секции вывода текущего блока Clinic.986987**`buffer`**988989Текстовый буфер, в котором можно сохранить текст для последующего использования. Текст, отправленный сюда, добавляется в конец любого существующего текста. Ошибка – оставить какой-либо текст в буфере после завершения обработки файла Clinic.990991**`file`**992993Отдельный «файл clinic», который будет автоматически создан Clinic. Выбранное имя файла – `{basename}.clinic{extension}`, где `basename` и `extension` получили вывод от `os.path.splitext()`, запущенного на текущем файле. (Пример: назначение `file` для `_pickle.c` будет записано в `_pickle.clinic.c`.)994995**Важно: при использовании** `file` **назначения** *обязательно проверьте* **сгенерированный файл!**996997**`two-pass`**998999Буфер, подобный10001001`buffer`10021003. Однако буфер двойного прохода можно сбросить только один раз, и он выводит весь текст, отправленный ему во время всей обработки, даже из блоков Clinic10041005*после*10061007точки сброса.10081009**`suppress`**10101011Текст подавляется – отбрасывается.10121013Clinic определяет пять новых директив, позволяющих перенастроить его вывод.10141015Первая новая директива – `dump`:10161017```c1018dump <destination>1019```10201021Эта директива сбрасывает текущее содержимое указанного назначения в вывод текущего блока и очищает его. Она работает только с назначениями `buffer` и `two-pass`.10221023Вторая новая директива – `output`. Самая простая форма `output` выглядит так:10241025```c1026output <field> <destination>1027```10281029Эта директива указывает Clinic выводить *поле* в *назначение*. `output` также поддерживает специальное мета-назначение под названием `everything`, которое указывает Clinic выводить *все* поля в это *назначение*.10301031`output` имеет ряд других функций:10321033```c1034output push1035output pop1036output preset <preset>1037```10381039`output push` и `output pop` позволяют помещать и извлекать конфигурации со внутреннего стека конфигураций, чтобы можно было временно изменить конфигурацию вывода, а затем легко восстановить предыдущую конфигурацию. Просто поместите текущую конфигурацию в стек перед изменением, чтобы сохранить её, а затем извлеките, когда потребуется восстановить предыдущую конфигурацию.10401041`output preset` устанавливает вывод Clinic в одну из нескольких встроенных предустановленных конфигураций следующим образом:10421043> **`block`**1044>1045> Исходная начальная конфигурация Clinic. Пишет всё сразу после входного блока.1046>1047> Подавляет `parser_prototype` и `docstring_prototype`, всё остальное записывает в `block`.1048>1049> **`file`**1050>1051> Предназначен для записи всего, что возможно, в «clinic-файл». Затем этот файл `#include` ближе к началу вашего файла. Возможно, потребуется перестроить файл, чтобы это заработало, но обычно для этого достаточно создать прямые объявления для различных определений `typedef` и `PyTypeObject`.1052>1053> Подавляет `parser_prototype` и `docstring_prototype`, записывает `impl_definition` в `block`, а всё остальное – в `file`.1054>1055> Имя файла по умолчанию – `"{dirname}/clinic/{basename}.h"`.1056>1057> **`buffer`**1058>1059> Сохраняет большую часть вывода Clinic для последующей записи в файл ближе к концу. Для файлов Python, реализующих модули или встроенные типы, рекомендуется сбрасывать буфер непосредственно перед статическими структурами для модуля или встроенного типа; обычно они находятся ближе к концу. Использование `buffer` может потребовать даже больше правок, чем `file`, если в файле есть статические массивы `PyMethodDef`, определённые в середине файла.1060>1061> Подавляет `parser_prototype`, `impl_prototype` и `docstring_prototype`, записывает `impl_definition` в `block`, а всё остальное – в `file`.1062>1063> **`two-pass`**1064>1065> Аналогичен предустановке `buffer`, но записывает прямые объявления в буфер `two-pass`, а определения – в `buffer`. Это похоже на предустановку `buffer`, но может потребовать меньше правок, чем `buffer`. Сбросьте буфер `two-pass` ближе к началу файла, а буфер `buffer` – ближе к концу, как при использовании предустановки `buffer`.1066>1067> Подавляет `impl_prototype`, записывает `impl_definition` в `block`, записывает `docstring_prototype`, `methoddef_define` и `parser_prototype` в `two-pass`, всё остальное записывает в `buffer`.1068>1069> **`partial-buffer`**1070>1071> Аналогичен предустановке `buffer`, но записывает больше вещей в `block`, а действительно большие фрагменты сгенерированного кода – только в `buffer`. Это полностью исключает проблему «определение до использования» предустановки `buffer`, ценой немного большего количества материала в выводе блока. Сбросьте буфер `buffer` ближе к концу, как при использовании предустановки `buffer`.1072>1073> Подавляет `impl_prototype`, записывает `docstring_definition` и `parser_definition` в `buffer`, всё остальное записывает в `block`.10741075Третья новая директива – `destination`:10761077```c1078destination <name> <command> [...]1079```10801081Эта директива выполняет операцию над назначением с именем `name`.10821083Определены две подкоманды: `new` и `clear`.10841085Подкоманда `new` работает следующим образом:10861087```c1088destination <name> new <type>1089```10901091Это создаёт новое назначение с именем `<name>` и типом `<type>`.10921093Существует пять типов назначений:10941095> **`suppress`**1096>1097> Отбрасывает текст.1098>1099> **`block`**1100>1101> Записывает текст в текущий блок. Именно так Clinic работал изначально.1102>1103> **`buffer`**1104>1105> Простой текстовый буфер, как встроенное назначение «buffer» выше.1106>1107> **`file`**1108>1109> Текстовый файл. Назначение «файл» принимает дополнительный аргумент – шаблон для построения имени файла, например:1110>1111> > назначение \<имя\> new \<тип\> \<шаблон\_файла\>1112>1113> Внутри шаблона можно использовать три строки, которые будут заменены на части имени файла:1114>1115> > **{path}**1116> >1117> > Полный путь к файлу, включая каталог и полное имя файла.1118> >1119> > **{dirname}**1120> >1121> > Имя каталога, в котором находится файл.1122> >1123> > **{basename}**1124> >1125> > Просто имя файла, без каталога.1126> >1127> > **{basename\_root}**1128> >1129> > Базовое имя без расширения (всё до последней точки, но не включая её).1130> >1131> > **{basename\_extension}**1132> >1133> > Последняя точка и всё после неё. Если в базовом имени нет точки, будет пустая строка.1134>1135> Если в имени файла нет точек, {basename} и {filename} совпадают, а {extension} пуст. «{basename}{extension}» всегда совпадает с «{filename}».1136>1137> **`two-pass`**1138>1139> Буфер двухпроходной обработки, как встроенное назначение «two-pass» выше.11401141Подкоманда `clear` работает следующим образом:11421143```c1144destination <name> clear1145```11461147Она удаляет весь накопленный текст до этой точки в назначении. (Не знаю, для чего это может понадобиться, но подумал, что может быть полезно для экспериментов.)11481149Четвёртая новая директива – `set`:11501151```c1152set line_prefix "string"1153set line_suffix "string"1154```11551156`set` позволяет задать две внутренние переменные в Clinic. `line_prefix` – строка, которая будет добавляться перед каждой строкой вывода Clinic; `line_suffix` – строка, которая будет добавляться после каждой строки вывода Clinic.11571158Обе поддерживают две строки формата:11591160> **`{block comment start}`**1161>1162> Превращается в строку1163>1164> `/*`1165>1166> , последовательность символов начала комментария для C-файлов.1167>1168> **`{block comment end}`**1169>1170> Превращается в строку1171>1172> `*/`1173>1174> , последовательность символов конца комментария для C-файлов.11751176Последняя новая директива – та, которую не нужно использовать напрямую, называется `preserve`:11771178```c1179preserve1180```11811182Это говорит Clinic, что текущее содержимое вывода следует оставить без изменений. Внутренне Clinic использует это при выгрузке вывода в файлы `file`; оборачивание в блок Clinic позволяет Clinic использовать существующую функциональность контрольных сумм, чтобы гарантировать, что файл не был изменён вручную перед его перезаписью.11831184### Трюк с #ifdef11851186При преобразовании функции, которая доступна не на всех платформах, можно воспользоваться трюком, чтобы немного упростить жизнь. Существующий код, вероятно, выглядит так:11871188```c1189#ifdef HAVE_FUNCTIONNAME1190static module_functionname(...)1191{1192...1193}1194#endif /* HAVE_FUNCTIONNAME */1195```11961197И затем в структуре `PyMethodDef` в нижней части существующий код будет содержать:11981199```text1200#ifdef HAVE_FUNCTIONNAME1201{'functionname', ... },1202#endif /* HAVE_FUNCTIONNAME */1203```12041205В этом случае тело функции impl нужно заключить внутрь `#ifdef`, вот так:12061207```c1208#ifdef HAVE_FUNCTIONNAME1209/*[clinic input]1210module.functionname1211...1212[clinic start generated code]*/1213static module_functionname(...)1214{1215...1216}1217#endif /* HAVE_FUNCTIONNAME */1218```12191220Затем удалите эти три строки из структуры `PyMethodDef`, заменив их макросом, сгенерированным Argument Clinic:12211222```c1223MODULE_FUNCTIONNAME_METHODDEF1224```12251226(Настоящее имя этого макроса можно найти в сгенерированном коде. Или можно вычислить его самостоятельно: это имя вашей функции, как оно определено в первой строке вашего блока, но с заменой точек на подчёркивания, в верхнем регистре и с добавлением `"_METHODDEF"` в конец.)12271228Возможно, вы задаётесь вопросом: что если `HAVE_FUNCTIONNAME` не определён? Тогда макрос `MODULE_FUNCTIONNAME_METHODDEF` тоже не будет определён!12291230Вот где Argument Clinic проявляет смекалку. Он обнаруживает, что блок Argument Clinic может быть деактивирован директивой `#ifdef`. Когда это происходит, он генерирует небольшой дополнительный код, который выглядит так:12311232```c1233#ifndef MODULE_FUNCTIONNAME_METHODDEF1234    #define MODULE_FUNCTIONNAME_METHODDEF1235#endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */1236```12371238Это означает, что макрос всегда работает. Если функция определена, это превращается в правильную структуру, включая завершающую запятую. Если функция не определена, это превращается в ничто.12391240Однако это вызывает одну деликатную проблему: куда Argument Clinic должен поместить этот дополнительный код при использовании пресета вывода "block"? Его нельзя поместить в выходной блок, потому что он может быть деактивирован `#ifdef`. (В этом вся суть!)12411242В этой ситуации Argument Clinic записывает дополнительный код в назначение "buffer". Это может означать, что Argument Clinic выдаст предупреждение:12431244```text1245Warning in file "Modules/posixmodule.c" on line 12357:1246Destination buffer 'buffer' not empty at end of file, emptying.1247```12481249Если это произошло, просто откройте файл, найдите блок `dump buffer`, который Argument Clinic добавил в файл (он будет в самом низу), и переместите его выше структуры `PyMethodDef`, где используется этот макрос.12501251### Использование Argument Clinic в файлах Python12521253На самом деле Argument Clinic можно использовать для предварительной обработки файлов Python. Конечно, использовать блоки Argument Clinic нет смысла, так как интерпретатор Python не поймёт вывод. Но использование Argument Clinic для выполнения блоков Python позволяет использовать Python в качестве препроцессора для Python!12541255Поскольку комментарии в Python отличаются от комментариев в C, блоки Argument Clinic, встроенные в файлы Python, выглядят немного иначе. Они выглядят так:12561257```python1258#/*[python input]1259#print("def foo(): pass")1260#[python start generated code]*/1261def foo(): pass1262#/*[python checksum:...]*/1263```1264