Инструкции по Клинике споров¶
Автор: | 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, вы действительно должны взять преобразование дальше, используя некоторые продвинутые понятия, которые вы будете видеть позже в документе (как «конвертеры возвращения» и «сам конвертеры»). Но мы будем держать это просто для этой траектории, чтобы вы могли учиться.
Давайте погрузимся!
Убедитесь, что вы работаете со свежеобновленным оформлением заказа багажника CPython.
Найдите встроенное Python, которое называет either:c:func:PyArg_ParseTuple или
PyArg_ParseTupleAndKeywords()
и не было преобразовано, чтобы работать с клиникой аргумента все же. Для моего примера я использую_pickle.Pickler.dump()
.Если вызов функции
PyArg_Parse
использует любую из следующих единиц форматирования:O& O! es es# et et#
или при наличии нескольких вызовов to:c:func:PyArg_ParseTuple следует выбрать другую функцию. Клиника аргумента does поддерживает все эти сценарии. Но это продвинутые темы - давайте сделаем что-то более простое для вашей первой функции.
Кроме того, если функция имеет несколько вызовов to:c:func:PyArg_ParseTuple или
PyArg_ParseTupleAndKeywords()
, где она поддерживает различные типы для одного и того же аргумента, или если функция использует что-то помимо функций PyArg_Parse для синтаксического анализа своих аргументов, она, вероятно, не подходит для преобразования в Клинике споров. Клиника аргументов не поддерживает общие функции или полиморфные параметры.Добавьте следующий шаблон над функцией, создавая наш блок:
/*[clinic input] [clinic start generated code]*/
Вырезать докстринг и вставить его между
[clinic]
строк, удалив все мусор, который делает его правильно цитируемой C строка. Когда вы закончите, вы должны иметь только текст, основанный на левом поле, без строки шире 80 символов. (Клиника аргументов сохранит отступы внутри докстринг.Если у старого докстринг была первая строка, которая выглядела как подпись функции, отбросьте эту строку. (докстринг больше не нуждается в этом - когда вы используете
help()
на вашем построении в будущем, первая строка будет построена автоматически на основе подписи функции.Образец:
/*[clinic input] Write a pickled representation of obj to the open file. [clinic start generated code]*/
5. Если у вашего докстринг нет «сводной» строки, Клинике споров будет жаловаться. Так что давайте убедимся, что он есть. Строка «summary» должна представлять собой абзац, состоящий из одной строки из 80 столбцов в начале докстринг.
(Наш пример докстринг состоит исключительно из сводной строки, поэтому образец код не должен меняться для этого шага.)
Над строкой документа введите имя функции, а затем пустую строку. Это должно быть названием Python функции и должно быть всем пунктирным путем к функции - это должно начаться с названия модуля, включать любые подмодули, и если функция - метод на класс, это должно включать имя класс также.
Образец:
/*[clinic input] _pickle.Pickler.dump Write a pickled representation of obj to the open file. [clinic start generated code]*/
Если это - первый раз, когда модуль или класс были используемый с клиникой аргумента в этом файле 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]*/
Объявите каждый из параметров функции. Каждый параметр должен иметь собственную строку. Все строки параметров должны иметь отступы от имени функции и докстринг.
Общий вид этих строк параметров следующий:
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]*/
Если функция имеет
|
в строке формата, то есть некоторые параметры имеют значения по умолчанию, его можно игнорировать. Клинике споров определяет необязательные параметры на основе того, имеют ли они значения по умолчанию.Если функция имеет
$
в строке формата, то есть принимает аргументы только для ключевых слов, укажите*
в строке перед первым аргументом только для ключевых слов, отступив то же, что и строки параметра.(
_pickle.Pickler.dump
не имеет ни одного, поэтому наш образец не изменился.)Если существующая функция 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]*/
Полезно написать докстринг за параметр для каждого параметра. Но 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]*/
Сохраните и закройте файл, затем управляйте
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"
Перепроверка, что аргумент-парсинг клиника аргумента код произвел взгляды в основном то же как существующий код.
First, ensure both places use the same argument-parsing function. The existing code must call either
PyArg_ParseTuple()
orPyArg_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 то же.Обратите внимание, что последней строкой вывода является объявление функции «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; ...
Помните макрос со структурой
PyMethodDef
для этой функции? Найти существующую структуруPyMethodDef
для этой функции и замените ее ссылкой на макрос. (Если сборка находится в области действия модуля, это, вероятно, будет очень близко к концу файла; если построение является методом класс, это, вероятно, будет ниже, но относительно близко к реализации.)Обратите внимание, что тело макроса содержит конечную запятую. Поэтому при замене существующей статической структуры
PyMethodDef
макросом * не * добавляйте запятую к концу.Образец:
static struct PyMethodDef Pickler_methods[] = { __PICKLE_PICKLER_DUMP_METHODDEF __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF {NULL, NULL} /* sentinel */ };
Компилируйте, затем выполните соответствующие части пакета регрессионных тестов. Это изменение не должно вводить никаких новых предупреждений или ошибок времени компиляции, и не должно быть видимых извне изменений поведения 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:...]*/