Инструкции по Клинике споров

Автор:Larry Hastings

Аннотация

Клиника аргументов является препроцессором для файлов CPython C. Его цель - автоматизировать весь образец, связанный с написанием аргумента парсинг код для «builtins». В этом документе показано, как преобразовать первую функцию C в работу с клиникой Клинике споров, а затем приведены некоторые дополнительные темы по использованию клиники Клинике споров.

В настоящее время Клинике споров считается внутренней только для CPython. Его использование не поддерживается для файлов за пределами CPython, и нет гарантий обратной совместимости для будущих версий. Другими словами: если вы поддерживаете внешнее расширение C для CPython, вы можете поэкспериментировать с Клинике споров в вашем собственном код. Но версия клиники аргумента, что суда со следующей версией CPython could быть полностью несовместимым и сломать весь ваш код.

Цели Клинике споров

Основная цель Клинике споров - взять на себя ответственность за все аргументы парсинг код внутри CPython. Это означает, что, когда вы преобразовываете функцию, чтобы работать с клиникой аргумента, что функция больше не должна делать ни одного своего собственного парсинга аргумента - код, произведенный клиникой аргумента, должен быть «черным ящиком» вам, где CPython призывает наверху, и ваш код называют внизу с PyObject *args (и, возможно, PyObject *kwargs) магически преобразованы в C переменных и типов, которые вам нужны.

Для того чтобы Клинике споров могла достичь своей основной цели, она должна быть простой в использовании. В настоящее время работая с аргументом CPYTHON библиотека парсинг - тяжелая работа, запрашивая поддерживающий избыточную информацию в удивительном количестве мест. Когда вы используете клинику аргументов, вам не нужно повторяться.

Очевидно, что никто не хотел бы использовать клинику аргументов, если она не решает их проблему - и не создает новых собственных проблем. Поэтому крайне важно, чтобы клиника аргументации генерировала правильные код. Было бы неплохо, если бы код тоже был быстрее, но, по крайней мере, он не должен вводить большую регрессию скорости. (В конечном счете клиника аргумента should делает главное ускорение возможным - мы могли переписать его код генератор, чтобы произвести сделанный на заказ аргумент кодекс парсинг, вместо того, чтобы назвать аргумент CPython общего назначения библиотекой парсинг. Это сделало бы для самого быстрого аргумента парсинг возможным!)

Кроме того, клиника аргументов должна быть достаточно гибкой, чтобы работать с любым подходом к аргументации парсинг. У Python есть некоторые функции с некоторыми очень странными поведениями парсинг; цель Клинике споров - поддержать их всех.

Наконец, первоначальная мотивация Клинике споров состояла в том, чтобы предоставить интроспективные «подписи» для CPython builtins. Это используемый, чтобы быть, функции вопроса самоанализа бросили бы исключение, если бы вы прошли во встроенном. С клиникой аргументов, это ушло в прошлое!

Одна идея, которую вы должны иметь в виду, когда вы работаете с Клинике споров: чем больше информации вы даете, тем лучше это будет делать. Клиника аргументов сейчас, по общему признанию, относительно проста. Но по мере развития он станет более сложным, и он должен быть способен делать много интересных и умных вещей со всей информацией, которую вы даете ему.

Основные понятия и использование

Клиника аргументов поставляется с CPython; вы найдете его в Tools/clinic/clinic.py. При выполнении этого сценария укажите файл C в качестве аргумента:

$ python3 Tools/clinic/clinic.py foo.c

Клиника аргументов будет сканировать файл в поисках строк, которые выглядят примерно так:

/*[clinic input]

Когда он находит его, он читает все до строки, которая выглядит в точности так:

[clinic start generated code]*/

Все между этими двумя линиями является входным для Клиники споров. Все эти строки, включая начальные и конечные строки комментариев, вместе называются «блоком» Клиники споров.

Когда Клинике споров анализирует один из этих блоков, он генерирует выходные данные. Эти выходные данные перезаписываются в C-файл сразу после блока с последующим комментарием, содержащим контрольную сумму. Блок Клиники споров теперь выглядит следующим образом:

/*[clinic input]
... clinic input goes here ...
[clinic start generated code]*/
... clinic output goes here ...
/*[clinic end generated code: checksum=...]*/

При повторном запуске Клинике споров в том же файле, Клинике споров отбросит старые выходные данные и запишет новые выходные данные с новой строкой контрольной суммы. Однако, если входные данные не изменились, выходные данные также не изменятся.

Не следует изменять выходную часть блока Клинике споров. Вместо этого измените входные данные до тех пор, пока они не будут выводить нужные данные. (Это цель контрольной суммы - определить, изменил ли кто-то выходные данные, так как эти изменения будут потеряны в следующий раз, когда Клинике споров опубликует новые выходные данные.)

Для ясности, вот терминология, которую мы будем использовать с Клинике споров:

  • Первой строкой комментария (/*[clinic input]) является start line.
  • Последней строкой начального комментария ([clinic start generated code]*/) является end line.
  • Последняя строка (/*[clinic end generated code: checksum=...]*/) - checksum line.
  • Между начальной и конечной линией находится input.
  • Между конечной строкой и строкой контрольной суммы находится строка output.
  • Весь текст вместе, от начальной строки до строки контрольной суммы включительно, является block. (Блок, который не был успешно обработан клиникой аргументов, еще не имеет выходных данных или строки контрольной суммы, но все еще считается блоком.)

Преобразование Вашей первой функции

Лучший способ понять, как работает Клинике споров, это преобразовать функцию в работу с ней. Вот минимальные шаги, которые вам нужно выполнить, чтобы преобразовать функцию в работу с аргументной клиникой. Обратите внимание, что для код вы планируете зарегистрироваться к CPython, вы действительно должны взять преобразование дальше, используя некоторые продвинутые понятия, которые вы будете видеть позже в документе (как «конвертеры возвращения» и «сам конвертеры»). Но мы будем держать это просто для этой траектории, чтобы вы могли учиться.

Давайте погрузимся!

  1. Убедитесь, что вы работаете со свежеобновленным оформлением заказа багажника CPython.

  2. Найдите встроенное Python, которое называет either:c:func:PyArg_ParseTuple или PyArg_ParseTupleAndKeywords() и не было преобразовано, чтобы работать с клиникой аргумента все же. Для моего примера я использую _pickle.Pickler.dump().

  3. Если вызов функции PyArg_Parse использует любую из следующих единиц форматирования:

    O&
    O!
    es
    es#
    et
    et#
    

    или при наличии нескольких вызовов to:c:func:PyArg_ParseTuple следует выбрать другую функцию. Клиника аргумента does поддерживает все эти сценарии. Но это продвинутые темы - давайте сделаем что-то более простое для вашей первой функции.

    Кроме того, если функция имеет несколько вызовов to:c:func:PyArg_ParseTuple или PyArg_ParseTupleAndKeywords(), где она поддерживает различные типы для одного и того же аргумента, или если функция использует что-то помимо функций PyArg_Parse для синтаксического анализа своих аргументов, она, вероятно, не подходит для преобразования в Клинике споров. Клиника аргументов не поддерживает общие функции или полиморфные параметры.

  4. Добавьте следующий шаблон над функцией, создавая наш блок:

    /*[clinic input]
    [clinic start generated code]*/
    
  5. Вырезать докстринг и вставить его между [clinic] строк, удалив все мусор, который делает его правильно цитируемой C строка. Когда вы закончите, вы должны иметь только текст, основанный на левом поле, без строки шире 80 символов. (Клиника аргументов сохранит отступы внутри докстринг.

    Если у старого докстринг была первая строка, которая выглядела как подпись функции, отбросьте эту строку. (докстринг больше не нуждается в этом - когда вы используете help() на вашем построении в будущем, первая строка будет построена автоматически на основе подписи функции.

    Образец:

    /*[clinic input]
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    

5. Если у вашего докстринг нет «сводной» строки, Клинике споров будет жаловаться. Так что давайте убедимся, что он есть. Строка «summary» должна представлять собой абзац, состоящий из одной строки из 80 столбцов в начале докстринг.

(Наш пример докстринг состоит исключительно из сводной строки, поэтому образец код не должен меняться для этого шага.)
  1. Над строкой документа введите имя функции, а затем пустую строку. Это должно быть названием Python функции и должно быть всем пунктирным путем к функции - это должно начаться с названия модуля, включать любые подмодули, и если функция - метод на класс, это должно включать имя класс также.

    Образец:

    /*[clinic input]
    _pickle.Pickler.dump
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
  2. Если это - первый раз, когда модуль или класс были используемый с клиникой аргумента в этом файле C, вы должны объявить модуль и/или класс. Надлежащая гигиена Клинике споров предпочитает объявлять их в отдельном блоке где-то в верхней части файла C, так же, как файлы и статика идут наверху. (В нашем типовом код мы просто покажем два блока друг рядом с другом.)

    Название класс и модуля должно совпасть с одним замеченным Python. При необходимости проверьте имя, определенное в PyModuleDef или PyTypeObject.

    Когда вы объявляете класс, вы должны также определить два аспекта его типа в C: декларация типа вы использовали бы для указателя на сущность этого класса и указателя на PyTypeObject для этого класс.

    Образец:

    /*[clinic input]
    module _pickle
    class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
    [clinic start generated code]*/
    
    /*[clinic input]
    _pickle.Pickler.dump
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
  3. Объявите каждый из параметров функции. Каждый параметр должен иметь собственную строку. Все строки параметров должны иметь отступы от имени функции и докстринг.

    Общий вид этих строк параметров следующий:

    name_of_parameter: конвертер
    

    Если параметр имеет значение по умолчанию, добавьте его после конвертера:

    name_of_parameter: converter = default_value
    

    Поддержка «значений по умолчанию» со стороны Клинике споров довольно изощренна; пожалуйста, см. the section below on default values для получения дополнительной информации.

    Добавьте пустую строку под параметрами.

    Что такое «конвертер»? он устанавливает как тип переменной используемый в C, так и метод для преобразования значения Python в значение C во время выполнения. На данный момент вы собираетесь использовать то, что называется «унаследованный конвертер» - удобный синтаксис, предназначенный для облегчения портирования старых код в клинику аргументов.

    Для каждого параметра скопировать «единицу формата» для того параметра от аргумента формата PyArg_Parse() и определить который как его конвертер как указанный строка. («единица формата» является официальным именем для один к трем подстрока символ параметра format, который говорит аргументу функцию парсинг, что тип переменной и как преобразовать его. Для получения дополнительной информации о единицах формата см. Анализ аргументов и сборка значений.)

    Для многосимвольных единиц, таких как z#, используйте все два или три символ строка.

    Образец:

     /*[clinic input]
     module _pickle
     class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
     [clinic start generated code]*/
    
     /*[clinic input]
     _pickle.Pickler.dump
    
        obj: 'O'
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
  4. Если функция имеет | в строке формата, то есть некоторые параметры имеют значения по умолчанию, его можно игнорировать. Клинике споров определяет необязательные параметры на основе того, имеют ли они значения по умолчанию.

    Если функция имеет $ в строке формата, то есть принимает аргументы только для ключевых слов, укажите * в строке перед первым аргументом только для ключевых слов, отступив то же, что и строки параметра.

    (_pickle.Pickler.dump не имеет ни одного, поэтому наш образец не изменился.)

  5. Если существующая функция C вызывает PyArg_ParseTuple() (в отличие от PyArg_ParseTupleAndKeywords()), то все её аргументы являются только позиционными.

    Чтобы отметить все параметры как позиционно-единственные в клинике аргумента, добавьте / на линии отдельно после последнего параметра, заказал то же как линии параметра.

    В настоящее время это все или ничего; либо все параметры являются только позиционными, либо ни один из них не является таковым. (В будущем Argument Clinic может ослабить это ограничение.

    Образец:

    /*[clinic input]
    module _pickle
    class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
    [clinic start generated code]*/
    
    /*[clinic input]
    _pickle.Pickler.dump
    
        obj: 'O'
        /
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
  6. Полезно написать докстринг за параметр для каждого параметра. Но per- parameter докстринги необязательны; вы можете пропустить этот шаг, если хотите.

    Вот то, как добавить докстринг за параметр. Первая линия докстринг за параметр должна быть заказана далее, чем определение параметра. Левое поле этой первой строки устанавливает левое поле для всей строки для каждого параметра; весь текст, который вы пишете, будет устарел на эту сумму. Вы можете писать столько текста, сколько хотите, через несколько строк, если хотите.

    Образец:

    /*[clinic input]
    module _pickle
    class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
    [clinic start generated code]*/
    
    /*[clinic input]
    _pickle.Pickler.dump
    
        obj: 'O'
            The object to be pickled.
        /
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
  7. Сохраните и закройте файл, затем управляйте Tools/clinic/clinic.py на нем. С удачей все worked—your блок теперь имеет выход, и файл .c.h был создан! снова откройте файл в текстовом редакторе для просмотра:

    /*[clinic input]
    _pickle.Pickler.dump
    
        obj: 'O'
            The object to be pickled.
        /
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
    static PyObject *
    _pickle_Pickler_dump(PicklerObject *self, PyObject *obj)
    /*[clinic end generated code: output=87ecad1261e02ac7 input=552eb1c0f52260d9]*/
    

    Очевидно, что если Клинике споров не производил никаких выходных данных, это потому, что он обнаружил ошибку в ваших входных данных. Исправляйте ошибки и повторите попытку до тех пор, пока Клинике споров не обработает файл без жалобы.

    Для удобства чтения большая часть клея код была создана в файле .c.h. Вам нужно будет включить это в ваш исходный файл .c, как правило, сразу после блока модуля клиники:

    #include "clinic/_pickle.c.h"
    
  8. Перепроверка, что аргумент-парсинг клиника аргумента код произвел взгляды в основном то же как существующий код.

    First, ensure both places use the same argument-parsing function. The existing code must call either PyArg_ParseTuple() or PyArg_ParseTupleAndKeywords(); ensure that the code generated by Клинике споров calls the exact same function.

    Во-вторых, формат строка, переданный в to:c:func:PyArg_ParseTuple или PyArg_ParseTupleAndKeywords(), должен быть exactly таким же, как и написанный вручную в существующей функции, вплоть до двоеточия или точки с запятой.

    (Клиника аргумента всегда производит свой формат строки с :, сопровождаемым под названием функция. Если формат существующего кода строка заканчивается на ;, чтобы предоставить справку по использованию, это изменение безвредно - не беспокойтесь об этом.)

    В-третьих, для параметров, единицы форматирования которых требуют два аргумента (например, переменная длины, строка кодировка или указатель на функцию преобразования), убедитесь, что второй аргумент является exactly одинаковым между двумя вызовами.

    В-четвертых, в выходной части блока вы найдете макрос препроцессора, определяющий подходящую статическую структуру PyMethodDef для этого построения:

    #define __PICKLE_PICKLER_DUMP_METHODDEF    \
    {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},
    

    Эта статическая структура должна быть exactly то же как существующая статическая PyMethodDef структура для этого встроенного.

    Если какой-либо из этих предметов отличается по any way, регулирует вашу спецификацию функции клиники аргумента и запускает повторно Tools/clinic/clinic.py пока они are то же.

  9. Обратите внимание, что последней строкой вывода является объявление функции «impl». Здесь идет реализация строителя. Удалите существующий прототип изменяемой функции, но оставьте проем фигурной скобки. Теперь удалите его аргумент парсинг код и объявления всех переменных, в которые он дампулирует аргументы. Заметьте, как аргументы Python - теперь аргументы этой функции impl; если реализация используемый различные имена для этих переменных, исправьте их.

    Давайте повторим, только потому, что это странно. Теперь твой код должен выглядеть вот так:

    static return_type
    your_function_impl(...)
    /*[clinic end generated code: checksum=...]*/
    {
    ...
    

    Клинике споров создал строку контрольной суммы и прототип функции непосредственно над ней. Необходимо написать открывающие (и закрывающие) фигурные скобки для функции и реализации внутри.

    Образец:

    /*[clinic input]
    module _pickle
    class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
    [clinic start generated code]*/
    /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
    
    /*[clinic input]
    _pickle.Pickler.dump
    
        obj: 'O'
            The object to be pickled.
        /
    
    Write a pickled representation of obj to the open file.
    [clinic start generated code]*/
    
    PyDoc_STRVAR(__pickle_Pickler_dump__doc__,
    "Write a pickled representation of obj to the open file.\n"
    "\n"
    ...
    static PyObject *
    _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)
    /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
    {
        /* Check whether the Pickler was initialized correctly (issue3664).
           Developers often forget to call __init__() in their subclasses, which
           would trigger a segfault without this check. */
        if (self->write == NULL) {
            PyErr_Format(PicklingError,
                         "Pickler.__init__() was not called by %s.__init__()",
                         Py_TYPE(self)->tp_name);
            return NULL;
        }
    
        if (_Pickler_ClearBuffer(self) < 0)
            return NULL;
    
        ...
    
  10. Помните макрос со структурой PyMethodDef для этой функции? Найти существующую структуру PyMethodDef для этой функции и замените ее ссылкой на макрос. (Если сборка находится в области действия модуля, это, вероятно, будет очень близко к концу файла; если построение является методом класс, это, вероятно, будет ниже, но относительно близко к реализации.)

    Обратите внимание, что тело макроса содержит конечную запятую. Поэтому при замене существующей статической структуры PyMethodDef макросом * не * добавляйте запятую к концу.

    Образец:

    static struct PyMethodDef Pickler_methods[] = {
        __PICKLE_PICKLER_DUMP_METHODDEF
        __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF
        {NULL, NULL}                /* sentinel */
    };
    
  11. Компилируйте, затем выполните соответствующие части пакета регрессионных тестов. Это изменение не должно вводить никаких новых предупреждений или ошибок времени компиляции, и не должно быть видимых извне изменений поведения Python’s.

    Ну, за исключением одной разницы: inspect.signature() запустить на своей функции должен теперь предоставить действительную подпись!

    Поздравляю, вы портировали свою первую функцию для работы с Клинике споров!

Продвинутые темы

Теперь, когда у вас есть опыт работы с Клинике споров, пришло время для расширенных тем.

Символические значения по умолчанию

Значением по умолчанию для параметра не может быть произвольное выражение. В настоящее время явно поддерживаются следующие параметры:

  • Числовые константы (целочисленные и плавающие)
  • строковые константы
  • True, False и None
  • Простые символьные константы, такие как sys.maxsize, которые должны начинаться с имени модуля

В случае, если вам любопытно, это реализовано в from_builtin() в Lib/inspect.py.

(В будущем, возможно, это необходимо еще более подробно проработать, чтобы позволить полные выражения, такие как CONSTANT - 1.)

Переименование C-функций и переменных, созданных Клиникой споров

Клиника аргументов автоматически называет функции, которые она создает для вас. Иногда это может вызвать проблему, если созданное имя конфликтует с именем существующей функции C. Существует простое решение: переопределить имена используемый для функций C. Просто добавьте ключевой "as" в строку объявления функции, а затем имя функции, которую вы хотите использовать. Клинике споров будет использовать это имя функции для базовой (сгенерированной) функции, а затем добавить "_impl" в конец и использовать его для имени функции impl.

Например, если мы хотим переименовать имена функций C, созданные для pickle.Pickler.dump, это будет выглядеть следующим образом:

/*[clinic input]
pickle.Pickler.dump as pickler_dumper

...

Теперь базовой функции будет присвоено имя pickler_dumper(), а функции impl - имя pickler_dumper_impl().

Точно так же у вас может быть проблема, где вы хотите дать параметру определенное имя Python, но то имя может быть неудобным в C. Клиника аргумента позволяет вам давать параметру различные имена в Python и в C, используя тот же синтаксис "as":

/*[clinic input]
pickle.Pickler.dump

    obj: object
    file as file_obj: object
    protocol: object = NULL
    *
    fix_imports: bool = True

Здесь имя используемый в Python (в сигнатуре и массиве keywords) будет равно file, но переменная C будет называться file_obj.

Это можно использовать для переименования параметра self!

Преобразование функций с помощью PyArg_UnpackTuple

Чтобы преобразовать функцию парсинг ее аргументы with:c:func:PyArg_UnpackTuple, просто запишите все аргументы, указав каждый как object. Для приведения типа в соответствие можно указать аргумент type. Все аргументы должны быть помечены как только позиционные (после последнего аргумента добавить / в строку самостоятельно).

В настоящее время сгенерированные код будут использовать PyArg_ParseTuple(), но это скоро изменится.

Опциональные группы

Некоторые унаследованные функции имеют сложный подход к парсинг своих аргументов: они подсчитывают количество позиционных аргументов, затем используют switch инструкция для вызова одного из нескольких different:c:func:PyArg_ParseTuple вызовов в зависимости от того, сколько позиционных аргументов есть. (Эти функции не могут принимать аргументы только для ключевых слов.) этот подход был используемый, чтобы моделировать дополнительные аргументы назад before:c:func:PyArg_ParseTupleAndKeywords, был создан.

Хотя функции, использующие этот подход, часто могут быть преобразованы в use:c:func:PyArg_ParseTupleAndKeywords, необязательные аргументы и значения по умолчанию, это не всегда возможно. У некоторых из этих устаревших функций есть behaviors:c:func:PyArg_ParseTupleAndKeywords, непосредственно не поддерживает. Наиболее очевидным примером является функция builtin range(), которая имеет необязательный аргумент на стороне left требуемого аргумента! другим примером является curses.window.addch(), который имеет группу из двух аргументов, которые всегда должны указываться вместе. (Аргументы называются x и y; если вы вызываете функцию, проходящую в x, вы также должны пройти в y - и если вы не проходите в x вы можете не пройти в y.)

В любом случае целью Клинике споров является поддержка аргумента парсинг для всех существующих построений CPython без изменения их семантики. Поэтому Клинике споров поддерживает этот альтернативный подход к разбору, используя то, что называется optional groups. Необязательные группы - это группы аргументов, которые должны передаваться вместе. Они могут находиться слева или справа от требуемых аргументов. Они могут only быть используемый с позиционно- единственными параметрами.

Примечание

Необязательные группы only предназначены для использования при преобразовании функций, которые делают несколько вызовов to:c:func:PyArg_ParseTuple! функции, которые используют any другой подход для аргументов парсинг, должны almost never быть преобразованными в клинику аргумента, используя дополнительные группы. Функции, использующие дополнительные группы, в настоящее время не могут иметь точных подписей в Python, потому что Python просто не понимает концепцию. По возможности избегайте использования дополнительных групп.

Чтобы указать дополнительную группу, добавьте [ в линию самостоятельно перед параметрами, которые требуется сгруппировать, и ] в строке самостоятельно после этих параметров. В качестве примера можно привести пример того, как curses.window.addch использует необязательные группы, чтобы сделать первые два параметра и последний параметр необязательными:

/*[clinic input]

curses.window.addch

    [
    x: int
      X-coordinate.
    y: int
      Y-coordinate.
    ]

    ch: object
      Character to add.

    [
    attr: long
      Attributes for the character.
    ]
    /

...

Примечания:

  • Для каждой дополнительной группы в функцию impl, представляющую группу, передается один дополнительный параметр. Параметр будет представлять собой int с именем group_{direction}_{number}, где {direction} является либо right, либо left в зависимости от того, находится группа до или после требуемых параметров, а {number} - монотонно увеличивающееся число (начиная с 1), указывающее, насколько далеко группа находится от требуемых параметров. При вызове impl этот параметр будет установлен на ноль, если эта группа не использовалась, и на ноль, если эта группа была используемый. (Под используемый или неиспользуемым я имею в виду, получали ли параметры аргументы в этом вызове.
  • Если необходимых аргументов нет, необязательные группы будут вести себя так, как если бы они находились справа от требуемых аргументов.
  • В случае неоднозначности аргумент парсинг код отдает предпочтение параметрам слева (перед требуемыми параметрами).
  • Дополнительные группы могут содержать только параметры только для позиций.
  • Дополнительные группы - only, предназначенный для наследия код. Не используйте дополнительные группы для новых код.

Использование реальных конвертеров Клиники споров вместо «устаревших конвероторов»

Чтобы сэкономить время и свести к минимуму количество знаний, необходимых для достижения первого порта в клинике Клинике споров, приведенная выше процедура говорит об использовании «устаревших преобразователей». «Устаревшие конвертеры» - удобство, предназначенное явно для облегчения переноса существующих код в клинику Argument. И чтобы было ясно, их использование приемлемо при портировании код для Python 3.4.

Однако в долгосрочной перспективе мы, вероятно, хотим, чтобы все наши блоки использовали реальный синтаксис Клинике споров для конвертеров. Почему? пару причин:

  • Правильные преобразователи гораздо легче читать и понятнее в их намерениях.
  • Некоторые единицы формата не поддерживаются как «устаревшие конвертеры», так как они требуют аргументов, а синтаксис устаревшего конвертера не поддерживает указание аргументов.
  • В будущем у нас может быть новый аргумент библиотека парсинг, которая не ограничена поддержками what:c:func:PyArg_ParseTuple; эта гибкость будет недоступна для параметров, использующих устаревшие преобразователи.

Поэтому, если вы не возражаете против дополнительных усилий, используйте обычные преобразователи вместо устаревших.

В двух словах синтаксис преобразователей Клинике споров (не устаревших) выглядит как вызов функции Python. Однако, если для функции нет явных аргументов (все функции принимают значения по умолчанию), скобки можно опустить. Таким образом, bool и bool() являются точно такими же преобразователями.

Все аргументы преобразователей Клинике споров доступны только для ключевых слов. Все конвертеры Клиники споров принимают следующие аргументы:

c_default
значение по умолчанию для этого параметра при определении в C. В частности, это будет инициализатор для переменной, объявленной в функции синтаксического анализа. Сведения об использовании см. в разделе the section on default values. Указано как строка.
annotation
значение аннотации для этого параметра. В настоящее время не поддерживается, поскольку PEP 8 требует, чтобы библиотека Python не использовала аннотации.

Кроме того, некоторые конвертеры принимают дополнительные аргументы. Вот список этих аргументов, наряду с их значениями:

accept

Набор типов Python (и, возможно, псевдотипов); это ограничивает допустимый аргумент Python значениями этих типов. (Это не объект общего назначения; как правило, он поддерживает только определенные списки типов, как показано в таблице устаревших преобразователей.)

Чтобы принять None, добавьте NoneType в этот набор.

bitwise
поддерживается только для беззнаковых целых чисел. Нативное целое значение этого аргумента Python будет записано в параметр без проверки диапазона, даже для отрицательных значений.
converter
Поддерживается только преобразователем object. Определяет название C «converter function», чтобы использовать, чтобы преобразовать этот объект в родной тип.
encoding
поддерживается только для строки. Определяет кодировка, чтобы использовать, преобразовывая этот строка из ул. Python (Unicode) стоимость в стоимость C char *.
subclass_of
поддерживается только для конвертера object. Требует, чтобы значение Python было подкласс типа Python, как выражено в с.
type
Поддерживается только для преобразователей object и self. Указывает тип C, который будет используемый для объявления переменной. Значение по умолчанию - "PyObject *".
zeroes
поддерживается только для строки. Если значение равно true, то встроенные байты NUL ('\\0') разрешены внутри значения. Длина строка передается функции impl сразу после параметра строка как параметр с именем <parameter_name>_length.

Обратите внимание, что не каждая возможная комбинация аргументов будет работать. Обычно эти аргументы реализуются конкретными PyArg_ParseTuple format units, со специфическим поведением. Например, в настоящее время невозможно вызвать unsigned_short без указания bitwise=True. Хотя вполне разумно думать, что это сработает, эти семантики не соответствуют какой-либо существующей единице формата. Так что клиника аргументов не поддерживает это. (Или, по крайней мере, еще нет.)

Ниже приведена таблица, показывающая отображение устаревших преобразователей в реальные преобразователи Клинике споров. Слева - устаревший конвертер, справа - текст, которым вы бы его заменили.

'B' unsigned_char(bitwise=True)
'b' unsigned_char
'c' char
'C' int(accept={str})
'd' double
'D' Py_complex
'es' str(encoding='name_of_encoding')
'es#' str(encoding='name_of_encoding', zeroes=True)
'et' str(encoding='name_of_encoding', accept={bytes, bytearray, str})
'et#' str(encoding='name_of_encoding', accept={bytes, bytearray, str}, zeroes=True)
'f' float
'h' short
'H' unsigned_short(bitwise=True)
'i' int
'I' unsigned_int(bitwise=True)
'k' unsigned_long(bitwise=True)
'K' unsigned_long_long(bitwise=True)
'l' long
'L' long long
'n' Py_ssize_t
'O' object
'O!' object(subclass_of='&PySomething_Type')
'O&' object(converter='name_of_c_function')
'p' bool
'S' PyBytesObject
's' str
's#' str(zeroes=True)
's*' Py_buffer(accept={buffer, str})
'U' unicode
'u' Py_UNICODE
'u#' Py_UNICODE(zeroes=True)
'w*' Py_buffer(accept={rwbuffer})
'Y' PyByteArrayObject
'y' str(accept={bytes})
'y#' str(accept={robuffer}, zeroes=True)
'y*' Py_buffer
'Z' Py_UNICODE(accept={str, NoneType})
'Z#' Py_UNICODE(accept={str, NoneType}, zeroes=True)
'z' str(accept={str, NoneType})
'z#' str(accept={str, NoneType}, zeroes=True)
'z*' Py_buffer(accept={buffer, str, NoneType})

В качестве примера, вот наш образец pickle.Pickler.dump с помощью правильного конвертера:

/*[clinic input]
pickle.Pickler.dump

    obj: object
        The object to be pickled.
    /

Write a pickled representation of obj to the open file.
[clinic start generated code]*/

Одним из преимуществ реальных преобразователей является то, что они более гибки, чем устаревшие преобразователи. Например, преобразователь unsigned_int (и все преобразователи unsigned_) может быть задан без bitwise=True. Их поведение по умолчанию выполняет проверку диапазона значения, и они не принимают отрицательные числа. Вы просто не можете сделать это с помощью унаследованного конвертера!

Клиника аргументов покажет вам все доступные конвертеры. Для каждого конвертера будут показаны все принятые параметры, а также значение по умолчанию для каждого параметра. Просто Tools/clinic/clinic.py --converters, которым управляют, чтобы видеть полный список.

Py_buffer

Используя конвертер Py_buffer (или 's*', 'w*', '*y' или устаревшие конвертеры 'z*'), вы must не call:c:func:PyBuffer_Release на обеспеченном буфере. Клинике споров генерирует код, который делает это для вас (в функции парсинг).

Продвинутые конвертеры

Помните те единицы формата, которые вы пропустили в первый раз, потому что они были продвинутыми? вот как с ними справиться.

Трюк в том, что все эти единицы формата принимают аргументы - либо функции преобразования, либо типы, либо строки, указывающие кодировка. (Но «устаревшие конвертеры» не поддерживают аргументы. Поэтому мы пропустили их для вашей первой функции.) аргумент, указанный для единицы измерения формата, теперь является аргументом для конвертера; этот аргумент является либо converter (для O&), subclass_of (для O!), либо encoding (для всех единиц формата, начинающихся с e).

При использовании subclass_of можно также использовать другой пользовательский аргумент для object(): type, который позволяет задать тип фактически используемый для параметра. Например, если необходимо убедиться, что объект является подкласс PyUnicode_Type, возможно, необходимо использовать конвертер object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type').

Одна из возможных проблем с использованием Клинике споров: она отнимает некоторую гибкость для единиц формата, начиная с e. При написании PyArg_Parse звонка вручную можно теоретически решить во время выполнения, что кодировка строка пройти в PyArg_ParseTuple(). Но теперь этот строка должен быть жестко закодирован во время предварительной обработки Argument- Clinic. Это ограничение носит преднамеренный характер; она значительно упростила поддержку этого блока форматирования и может позволить оптимизацию в будущем. Это ограничение не кажется необоснованным; сам CPython всегда передает статические жестко закодированные кодировка строки для параметров, единицы форматирования которых начинаются с e.

Значения параметра по умолчанию

Значениями по умолчанию для параметров могут быть любые из нескольких значений. В простейшем случае они могут быть строковыми, int или плавающими литералами:

foo: str = "abc"
bar: int = 123
bat: float = 45.6

Они могут также использовать любой из встроенных constants: Python’s

yep:  bool = True
nope: bool = False
nada: object = None

Также существует специальная поддержка значения по умолчанию NULL и простых выражений, задокументированных в следующих разделах.

Значение по умолчанию NULL

Для параметров строка и объекта можно задать значение None, чтобы указать, что по умолчанию нет. Однако это означает, что переменная C будет инициализирована как Py_None. Для удобства существует специальное значение, называемое NULL только по этой причине: с точки зрения Python’s оно ведет себя как значение по умолчанию None, но переменная C инициализируется с помощью NULL.

Выражения, указанные в качестве значений по умолчанию

Значение по умолчанию для параметра может быть больше, чем просто значением литерал. Это может быть все выражение, израсходовав математических операторов и смотря атрибуты на объектах. Однако эта поддержка не совсем проста, из-за какой-то неочевидной семантики.

Рассмотрим следующий пример:

foo: Py_ssize_t = sys.maxsize - 1

sys.maxsize могут иметь разные значения на разных платформах. Поэтому Argument Clinic не может просто вычислить это выражение локально и hard-код его в C. Поэтому оно сохраняет значение по умолчанию таким образом, что оно будет оцениваться во время выполнения, когда пользователь запрашивает подпись функции.

Какое пространство имен доступно при вычислении выражения? он оценивается в контекст модуля, из которого был создан. Таким образом, если модуль имеет атрибут под названием «max_widgets,», вы можете просто использовать его:

foo: Py_ssize_t = max_widgets

Если символ не найден в текущем модуле, он не может выполнить поиск в sys.modules. Это - то, как это может найти sys.maxsize, например. (Так как вы не знаете заранее, какие модули пользователь загрузит в их переводчика, лучше ограничивать себя модулями, которые предварительно загружены самим Python.)

Оценка значений по умолчанию только во время выполнения означает, что клиника аргументов не может вычислить правильное эквивалентное значение C по умолчанию. Так что нужно сказать это прямо. Когда вы используете выражение, вы должны также определить эквивалентное выражение в C, используя параметр c_default для converter:

foo: Py_ssize_t(c_default="PY_SSIZE_T_MAX - 1") = sys.maxsize - 1

Еще одно осложнение: Клинике споров не может заранее знать, действительно ли предоставленное вами выражение. Он разбирает его, чтобы убедиться, что он выглядит легальным, но он не может actually знать. При использовании выражений для указания значений, которые гарантированно являются допустимыми во время выполнения, необходимо соблюдать осторожность!

Наконец, поскольку выражения должны быть представлены как статические значения C, существует много ограничений на легальные выражения. Вот список функций Python, которые вам запрещено использовать:

  • Вызовы функции.
  • Встроенная, если инструкции (3 if foo else 5).
  • Автоматическое распаковывание последовательности (*[1, 2, 3]).
  • Список/набор/дикт понятий и выражений генератор.
  • Литералы Tuple/list/set/dict.

Использование возвращающего конвертера

По умолчанию клиника аргумента функции impl производит для вас, возвращает PyObject *. Но функция C часто вычисляет некоторый тип C, затем преобразует его в PyObject * в последний момент. Клинике споров обрабатывает преобразование входных данных из типов Python в собственные типы C - почему бы не преобразовать возвращаемое значение из собственного типа C в тип Python?

Вот что делает «конвертер возврата». Это изменяет вашу функцию impl, чтобы возвратить некоторый тип C, затем добавляет код к произведенному (non- impl) функция, чтобы обращаться с преобразованием, которые оценивают в соответствующий PyObject *.

Синтаксис возвращаемых преобразователей аналогичен синтаксису преобразователей параметров. Конвертер возврата задается так же, как и аннотация возврата самой функции. Возвращаемые преобразователи ведут себя так же, как преобразователи параметров; они принимают аргументы, все аргументы являются только ключевыми словами, и если вы не изменяете ни один из аргументов по умолчанию, вы можете опустить круглые скобки.

(Если вы используете оба "as" and конвертер возвращения для вашей функции, "as" должен прибыть перед конвертером возвращения.)

Существует еще одно усложнение при использовании преобразователей возврата: как указать на ошибку? обычно функция возвращает допустимый (non-NULL) указатель для успеха и NULL для сбоя. Но если вы используете целочисленный возвращаемый конвертер, все целые числа действительны. Как клиника аргументов может обнаружить ошибку? его решение: каждый конвертер возврата неявно ищет специальное значение, указывающее на ошибку. Если вы возвращаете ту стоимость, и ошибка была установлена (PyErr_Occurred() возвращает истинное значение), то произведенный код размножит ошибку. В противном случае оно кодирует возвращаемое значение как нормальное.

В настоящее время Клинике споров поддерживает только несколько преобразователей возврата:

bool
int
unsigned int
long
unsigned int
size_t
Py_ssize_t
float
double
DecodeFSDefault

Ни один из этих параметров не принимает. Для первых трех значений введите -1 для указания ошибки. Для DecodeFSDefault тип возврата - const char *; возвращает указатель NULL для указания ошибки.

(Есть также экспериментальный конвертер NoneType, который позволяет вам возвратить Py_None на успехе, или NULL на неудаче, не имея необходимость увеличивать ссылку рассчитывают на Py_None. Я не уверен, что это добавляет достаточно ясности, чтобы быть стоящим использовать.)

Для просмотра всех возвращаемых преобразователей Клинике споров поддерживает, вместе с их параметрами (если таковые имеются), просто запустите Tools/clinic/clinic.py --converters для полного списка.

Клонирование существующих функций

Если у вас есть ряд функций, которые выглядят похожими, вы можете использовать функцию «клона» клиники. При клонировании существующей функции повторно используется:

  • его параметры, включительн
    • их имена
    • их преобразователи, со всеми параметрами,
    • их значения по умолчанию
    • их строки для каждого параметра,
    • их kind (позиционны ли они только, позиционны или только ключевое слово или ключевой), и
  • его конвертер возвращения.

Единственное, что не копируется из исходной функции, - это ее документирование; синтаксис позволяет указать новый докстринг.

Вот синтаксис клонирования функции:

/*[clinic input]
module.class.new_function [as c_basename] = module.class.existing_function

Docstring for new_function goes here.
[clinic start generated code]*/

(Функции могут быть в различных модулях или классы. Я написал module.class в образце только, чтобы проиллюстрировать, что вы должны использовать весь путь к функциям both.)

К сожалению, нет синтаксиса для частичного клонирования функции или клонирования функции, а затем ее изменения. Клонирование - это предложение всего или ничего.

Кроме того, функция, из которой выполняется клонирование, должна быть предварительно определена в текущем файле.

Запрос кода Python

В остальных расширенных разделах необходимо написать Python код, который находится в файле C и изменяет состояние среды выполнения Argument Clinic. Это просто: вы просто определяете блок Python.

Блок Python использует различные линии разделителя, чем функциональный блок клиники аргумента. Похоже на это:

/*[python input]
# python code goes here
[python start generated code]*/

Все код внутри блока Python выполняется в момент разбора. Весь текст, записанный в stdout внутри блока, перенаправляется в «output» после блока.

В качестве примера, вот блок Python, который добавляет статическую целочисленную переменную к коду C:

/*[python input]
print('static int __ignored_unused_variable__ = 0;')
[python start generated code]*/
static int __ignored_unused_variable__ = 0;
/*[python checksum:...]*/

Использование «самостоятельного конвертера»

Клинике споров автоматически добавляет параметр «я» для вас с помощью конвертера по умолчанию. Это автоматически устанавливает type этого параметра к «указателю на случай», вы определили, когда вы объявили тип. Однако вы можете переопределить конвертер Клинике споров и указать его самостоятельно. Просто добавьте свой собственный параметр self в качестве первого параметра в блоке и убедитесь, что его конвертер является сущность self_converter или его подкласс.

Какой смысл? это позволяет переопределить тип self или присвоить ему другое имя по умолчанию.

Как вы определяете пользовательский тип, к которому вы хотите бросить self? если у вас только есть одна или две функции с тем же типом для self, вы можете непосредственно использовать существующий конвертер клиники аргумента self, проходящий в типе, который вы хотите использовать в качестве параметра type:

/*[clinic input]

_pickle.Pickler.dump

  self: self(type="PicklerObject *")
  obj: object
  /

Write a pickled representation of the given object to the open file.
[clinic start generated code]*/

С другой стороны, если у вас много функций, которые будут использовать тот же тип для self, лучше всего создать свой собственный конвертер, подклассифицируя self_converter, но перезаписывая type член:

/*[python input]
class PicklerObject_converter(self_converter):
    type = "PicklerObject *"
[python start generated code]*/

/*[clinic input]

_pickle.Pickler.dump

  self: PicklerObject
  obj: object
  /

Write a pickled representation of the given object to the open file.
[clinic start generated code]*/

Запись пользовательского конвертера

Как мы намекали в предыдущем разделе… вы можете писать собственные конвертеры! конвертер - это просто Python класс, наследующий от CConverter. Основное назначение пользовательского конвертера - если у вас есть параметр, использующий единицу измерения формата O& - парсинг этот параметр означает вызов a:c:func:PyArg_ParseTuple «функция конвертера».

Ваш конвертер класс нужно назвать *something*_converter. Если имя будет следовать этой конвенции, то ваш конвертер класс будет автоматически зарегистрирован в клинике аргумента; его именем будет имя вашего класс с отброшенным суффиксом _converter. (Это выполняется с помощью метакласс.)

Вы не были должны подкласс CConverter.__init__. Вместо этого следует написать функцию converter_init(). converter_init() всегда принимает параметр self; после этого, все дополнительные параметры must быть только для ключевого слова. Все аргументы, переданные конвертеру в клиника споров, будут переданы вашему converter_init().

Есть несколько дополнительных членов CConverter, которые вы можете указать в вашем подкласс. Вот текущий список:

type
тип C, используемый для этой переменной. type должен быть Python строка, определяющим тип, например int. Если это тип указателя, тип строка должен заканчиваться на ' *'.
default
значение Python по умолчанию для этого параметра в качестве значения Python. Или магическое значение unspecified, если нет значения по умолчанию.
py_default
default, как это должно отображаться в коде Python, как строка. Или None, если нет значения по умолчанию.
c_default
default, как это должно отображаться в коде C, как строка. Или None, если нет значения по умолчанию.
c_ignored_default
значение по умолчанию используемый для инициализации переменной C при отсутствии значения по умолчанию, но отсутствие указания значения по умолчанию может привести к предупреждению «неинициализированная переменная». Это может легко произойти при использовании групп опций - хотя правильно записанные код никогда не будут фактически использовать это значение, переменная действительно передается в impl, и компилятор C будет жаловаться на «использование» неинициализированного значения. Это значение всегда должно быть непустым строка.
converter
Имя функции преобразователя C как строка.
impl_by_reference
логическое значение. Если значение равно true, то при передаче переменной в функцию impl программа Клинике споров добавляет & перед именем переменной.
parse_by_reference
Логическое значение. Если значение равно true, то при передаче переменной & клиника аргументов добавляет into:c:func:PyArg_ParseTuple перед именем переменной.

Вот простейший пример пользовательского конвертера, от Modules/zlibmodule.c:

/*[python input]

class ssize_t_converter(CConverter):
    type = 'Py_ssize_t'
    converter = 'ssize_t_converter'

[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=35521e4e733823c7]*/

Этот блок добавляет конвертер в клинику аргументов с именем ssize_t. Параметры, объявленные как ssize_t, будут объявлены как тип Py_ssize_t и будут проанализированы блоком формата 'O&', который вызовет функцию преобразователя ssize_t_converter. Переменные ssize_t автоматически поддерживают значения по умолчанию.

Более современные пользовательские конвертеры могут вставить произвольный C код, чтобы обращаться с инициализацией и очисткой. В дереве источников CPython можно увидеть больше примеров пользовательских преобразователей; выберите файлы C для строка CConverter.

Запись пользовательского конвертера возврата

Написание пользовательского конвертера возврата во многом похоже на написание пользовательского конвертера. Только это несколько проще, потому что обратные преобразователи сами по себе намного проще.

Возвратитесь конвертеры должны подкласс CReturnConverter. Нет никаких примеров еще ольовательских конвертеров возвращения, потому что они еще не широко используемый. Если вы хотите написать свой собственный конвертер возврата, пожалуйста, прочитайте Tools/clinic/clinic.py, в частности, реализацию CReturnConverter и все его подклассы.

METH_O и METH_NOARGS

Чтобы преобразовать функцию с помощью METH_O, убедитесь, что единственный аргумент функции использует преобразователь object, и отметьте аргументы как только позиционные:

/*[clinic input]
meth_o_sample

     argument: object
     /
[clinic start generated code]*/

Чтобы преобразовать функцию с помощью METH_NOARGS, просто не указывайте аргументы.

По-прежнему можно использовать самопреобразователь, конвертер возврата и указать аргумент type в преобразователе объектов для METH_O.

Функции tp_new и tp_init

Функции tp_new и tp_init можно преобразовать. Просто назовите их __new__ или __init__ по мере необходимости. Примечания:

  • Имя функции, произведенное для __new__, не заканчивается в __new__ как он, был бы по умолчанию. Это просто имя класса, преобразованное в действительный идентификатор C.
  • Нет PyMethodDef #define произведен для этих функций.
  • __init__ функции возвращают int, а не PyObject *.
  • Используйте докстринг в качестве класс докстринг.
  • Хотя функции __new__ и __init__ всегда должны принимать объекты args и kwargs, при преобразовании можно указать любую подпись для этих функций. (Если ваша функция не поддержит ключевые слова, то произведенная функция парсинг бросит исключение, если это примет кого-либо.)

Изменение и перенаправление выходных данных клиники

Может быть неудобно, если результат клиники перемежается с вашим обычным редактируемым вручную C код. К счастью, Clinic можно настроить: вы можете буферизовать ее выходные данные для печати позже (или раньше!) или записать их в отдельный файл. Можно также добавить префикс или суффикс к каждой строке сгенерированных выходных данных клиники.

В то время как изменение продукции клиники этим способом может быть благом для удобочитаемости, это может привести к клинике код, используя типы, прежде чем они будут определены, или ваш код, пытающийся использовать произведенный клиникой код, прежде чем это будет определено. Эти проблемы могут быть легко решены, перестроив декларации в вашем файле или переместившись, где клиника произвела код, идет. (Вот почему поведение клиники по умолчанию заключается в выводе всего в текущий блок; в то время как многие люди считают, что это мешает удобочитаемости, это никогда не потребует перегруппировки вашего код, чтобы исправить проблемы определения перед использованием.)

Начнем с определения терминологии:

field

в этом контексте поле A является подразделом результатов работы клиники. Например, #define для структуры PyMethodDef является полем, называемым methoddef_define. Клиника имеет семь различных полей, которые она может вывести для определения функции:

docstring_prototype
docstring_definition
methoddef_define
impl_prototype
parser_prototype
parser_definition
impl_definition

Все имена имеют форму "<a>_<b>", где "<a>" - семантический представленный объект (функция парсинг, функция impl, docstring или methoddef структура), и "<b>" представляет, какой инструкция поле. Имена полей, которые заканчиваются на "_prototype", представляют собой прямые объявления этой вещи, без фактического тела/данных вещи; имена полей, которые заканчиваются на "_definition", представляют фактическое определение вещи, с телом/данными вещи. ("methoddef" особенный, он единственный, который заканчивается на "_define", представляя, что это препроцессор #define.)

destination

Место назначения - это место, куда клиника может записать выходные данные. Есть пять встроенных пунктов назначения:

block
Место назначения по умолчанию: напечатано в разделе вывода текущего блока клиники.
buffer
Буфер текста, в котором можно сохранить текст в дальнейшем. Отправленный здесь текст добавляется к концу любого существующего текста. Это ошибка, когда какой-либо текст остается в буфере, когда клиника заканчивает обработку файла.
file

Отдельный «файл клиники», который будет автоматически создан клиникой. Имя файла, выбранное для файла, является {basename}.clinic{extension}, где basename и extension назначили, продукция от os.path.splitext() работает на текущем файле. (Пример: место назначения file для _pickle.c было бы написано _pickle.clinic.c.)

Важно: при использовании file вашего пункт назначения, необходимо зарегистрироваться в сгенерированном файле!

two-pass
Буфер типа buffer. Однако буфер с двумя проходами может только быть свален однажды, и он распечатывает весь текст, посланный в него во время всей обработки, даже от after блоков клиники точка демпинга.
suppress
Текст подавляется - выбрасывается.

Клиника определяет пять новых директив, которые позволяют перенастроить ее выход.

Первая новая директива dump:

dump <destination>

При этом текущее содержимое именованного адресата выгружается в выходные данные текущего блока и очищается. Это работает только с buffer и two-pass адресатами.

Вторая новая директива output. Самая основная форма output такова:

output <field> <destination>

Это предписывает клинике выводить field в destination. output также поддерживает специальный мета-адресат под названием everything, который предписывает клинике выводить поля all в этот destination.

output имеет ряд других функций:

output push
output pop
output preset <preset>

output push и output pop позволяют нажимать и всплывать конфигурации во внутреннем стеке конфигурации, чтобы можно было временно изменить конфигурацию вывода, а затем легко восстановить предыдущую конфигурацию. Просто нажмите перед внесением изменений, чтобы сохранить текущую конфигурацию, а затем нажмите, когда вы хотите восстановить предыдущую конфигурацию.

output preset устанавливает выход клиники в одну из нескольких встроенных конфигураций предустановленной конфигурации следующим образом:

Оригинальная стартовая конфигурация клиники block. Записывает все сразу после входного блока.

Подавить parser_prototype и docstring_prototype, написать все остальное block.

file предназначен для записи всего в «файл клиники», что он может. Вы тогда #include этот файл около верхней части вашего файла. Для этого может потребоваться переупорядочить файл, но обычно это означает создание прямых объявлений для различных определений typedef и PyTypeObject.

Подавить parser_prototype и docstring_prototype, написать impl_definition к block, а все остальное написать к file.

Имя файла по умолчанию - "{dirname}/clinic/{basename}.h".

buffer Сохраните большую часть выходных данных клиники для записи в файл ближе к концу. Для файлов Python, реализующих модули или типы builtin, рекомендуется выгрузить буфер непосредственно над статическими структурами для модуля или типа builtin; они обычно очень близки к концу. Использование buffer может потребовать еще большего редактирования, чем file, если файл имеет статические массивы PyMethodDef, определенные в середине файла.

Подавить parser_prototype, impl_prototype и docstring_prototype, написать impl_definition к block, а все остальное написать к file.

two-pass аналогично шаблону buffer, но записывает объявления пересылки в буфер two-pass, а определения - в buffer. Это похоже на предустановку buffer, но может потребовать меньше редактирования, чем buffer. Свалите буфер two-pass около верхней части вашего файла и свалите buffer около конца точно так же, как вы были бы, используя заданный buffer.

Подавляет impl_prototype, писать impl_definition к block, писать docstring_prototype, methoddef_define, а parser_prototype к two-pass, писать все остальное к buffer.

partial-buffer аналогично предустановке buffer, но записывает больше вещей в block, только написав действительно большой чанки генерируемых код buffer. Это избегает проблемы определения перед использованием buffer полностью по маленькой стоимости наличия немного большего количества материала в продукции блока. Свалите buffer около конца, точно так же, как вы были бы, используя заданный buffer.

Подавляет impl_prototype, пишет docstring_definition и parser_definition к buffer, пишет все остальное к block.

Третья новая директива destination:

destination <name> <command> [...]

Выполняется операция с адресатом с именем name.

Существует две определенные подкоманды: new и clear.

new подкомандуют работами как this:

destination <name> new <type>

При этом создается новый конечный объект с именем <name> и типом <type>.

Существует пять типов назначения:

suppress выбрасывает текст.

block записывает текст в текущий блок. Это то, что клиника изначально сделала.

buffer простой текстовый буфер, как «буфер», построенный выше.

file текстовый файл. Назначение файла принимает дополнительный аргумент, шаблон для построения имени файла, например:

destination <name> new <type> <file_template>

Шаблон может использовать три строки внутренне, которые будут заменены частями filename:

{path}
The full path to the file, including directory and full filename.
{dirname}
The name of the directory the file is in.
{basename}
Just the name of the file, not including the directory.
{basename_root}
Basename with the extension clipped off (everything up to but not including the last „.“).
{basename_extension}
The last „.“ and everything after it. If the basename does not contain a period, this will be the empty string.

Если в имени файла нет точек, {basename} и {filename} совпадают, а {extension} пуст. «{basename} {extension}» всегда совпадает с «{filename}».

two-pass двухпроходный буфер, как и «двухпроходное» назначение сборки выше.

clear подкомандуют работами как this:

destination <name> clear

Он удаляет весь накопленный текст до этого момента в месте назначения. (Я не знаю, для чего вам это нужно, но я подумал, что, возможно, это было бы полезно, пока кто-то экспериментирует.

Четвертая новая директива set:

set line_prefix "string"
set line_suffix "string"

set позволяет задать две внутренние переменные в клинике. line_prefix является строка, который будет добавлен к каждой строке результатов клиники; line_suffix является строка, который будет добавлен к каждой строке результатов клиники.

Оба из них поддерживают две строки формата:

{block comment start} превращается в строка /*, текстовую последовательность начального комментария для C-файлов.

{block comment end} превращается в строка */, конечную текстовую последовательность для C-файлов.

Окончательная новая директива, которую вы не должны использовать напрямую, называется preserve:

preserve

Это говорит клинике, что текущее содержание выходных данных должно быть сохранено, без изменений. Это - используемый внутренне клиникой когда продукция демпинга в файлы file; помещение его в блок клиники позволяет клинике использовать существующую функцию контрольной суммы, чтобы убедиться, что файл не был изменен вручную до его перезаписи.

Уловка #ifdef

Если вы преобразуете функцию, которая доступна не на всех платформах, есть трюк, который вы можете использовать, чтобы сделать жизнь немного проще. Существующий код, вероятно, выглядит так:

#ifdef HAVE_FUNCTIONNAME
static module_functionname(...)
{
...
}
#endif /* HAVE_FUNCTIONNAME */

И тогда в структуре PyMethodDef внизу существующий код будет иметь:

#ifdef HAVE_FUNCTIONNAME
{'functionname', ... },
#endif /* HAVE_FUNCTIONNAME */

В этом сценарии, вы должны заключить тело вашей функции импл внутри #ifdef, как так:

#ifdef HAVE_FUNCTIONNAME
/*[clinic input]
module.functionname
...
[clinic start generated code]*/
static module_functionname(...)
{
...
}
#endif /* HAVE_FUNCTIONNAME */

Затем удалите эти три строки из структуры PyMethodDef, заменив их макросом Клинике споров generated:

MODULE_FUNCTIONNAME_METHODDEF

(Вы можете найти настоящее имя для этого макроса в произведенном код. Или вы можете вычислить его самостоятельно: это название вашей функции, как определено в первой строке вашего блока, но с периодами, измененными на подчеркивания, вверх, и "_METHODDEF" добавлено к концу.

Возможно, вам интересно: а что, если HAVE_FUNCTIONNAME не определен? макрос MODULE_FUNCTIONNAME_METHODDEF также не будет определен!

Вот где клиника аргументов становится очень умной. Он фактически обнаруживает, что блок Клинике споров может быть деактивирован #ifdef. Когда это происходит, это генерирует немного дополнительных код, которые выглядят так:

#ifndef MODULE_FUNCTIONNAME_METHODDEF
    #define MODULE_FUNCTIONNAME_METHODDEF
#endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */

Это означает, что макрос всегда работает. Если функция определена, она превращается в правильную структуру, включая конечную запятую. Если функция не определена, это не превращается в ничего.

Тем не менее, это вызывает одну щекотливую проблему: где Клинике споров должна поставить эту дополнительную код при использовании предустановки «блочного» вывода? он не может войти в выходной блок, потому что он может быть деактивирован #ifdef. (В этом весь смысл!)

В этой ситуации клиника аргумента пишет дополнительный код «буферному» месту назначения. Это может означать, что вы получите жалобу от Argument Clinic:

Предупреждение в файле "Modules/posixmodule.c" в строке 12357: Буфер назначения
"buffer" не пуст в конце файла, опорожнение.

Когда это происходит, просто откройте свой файл, найдите, что dump buffer блокируют ту клинику аргумента, добавленную к вашему файлу (это будет в самом основании), затем переместите его выше структуры PyMethodDef, где тот макрос - используемый.

Использование Клиники споров в файлах Python

На самом деле возможно использовать клинику аргумента, чтобы предварительно обработать файлы Python. Нет смысла использовать блоки Клинике споров, конечно, так как выходные данные не имеют смысла для Python интерпретатор. Но использование клиники аргумента, чтобы управлять блоками Python позволяет вам использовать Python в качестве препроцессора Python!

Поскольку комментарии Python отличаются от комментариев C, блоки Argument Clinic, встроенные в файлы Python, выглядят несколько иначе. Они выглядят так:

#/*[python input]
#print("def foo(): pass")
#[python start generated code]*/
def foo(): pass
#/*[python checksum:...]*/