Добавить подпись с аннотациями к методам расширения - PullRequest
0 голосов
/ 26 мая 2018

При встраивании Python в мое приложение и написании типа расширения я могу добавить signature к методу, используя правильно созданную строку .tp_doc.

static PyMethodDef Answer_methods[] = {
  { "ultimate", (PyCFunction)Answer_ultimate, METH_VARARGS, 
    "ultimate(self, question='Life, the universe, everything!')\n"
    "--\n"
    "\n"
    "Return the ultimate answer to the given question." },
  { NULL }
};

Когда help(Answer)выполняется, возвращается следующее (сокращенно):

class Answer(builtins.object)
 |
 |  ultimate(self, question='Life, the universe, everything!')
 |      Return the ultimate answer to the given question.

Это хорошо, но я использую Python3.6, который поддерживает аннотации.Я хотел бы аннотировать вопрос, чтобы быть строкой, и функция, чтобы возвратить int.Я попытался:

static PyMethodDef Answer_methods[] = {
  { "ultimate", (PyCFunction)Answer_is_ultimate, METH_VARARGS, 
    "ultimate(self, question:str='Life, the universe, everything!') -> int\n"
    "--\n"
    "\n"
    "Return the ultimate answer to the given question." },
  { NULL }
};

, но это возвращается к нотации (...), и документация становится такой:

 |  ultimate(...)
 |      ultimate(self, question:str='Life, the universe, everything!') -> int
 |      --
 |
 |      Return the ultimate answer to the given question.

, и запрос inspect.signature(Answer.ultimate) приводит к исключению.

Traceback (most recent call last):
  File "<string>", line 11, in <module>
  File "inspect.py", line 3037, in signature
  File "inspect.py", line 2787, in from_callable
  File "inspect.py", line 2266, in _signature_from_callable
  File "inspect.py", line 2090, in _signature_from_builtin
ValueError: no signature found for builtin <built-in method ultimate of example.Answer object at 0x000002179F3A11B0>

Я пытался добавить аннотации после факта с кодом Python:

example.Answer.ultimate.__annotations__ = {'return': bool}

Но дескрипторы встроенного метода не могут добавлять аннотации таким образом.

Traceback (most recent call last):
  File "<string>", line 2, in <module>
AttributeError: 'method_descriptor' object has no attribute '__annotations__'

Есть ли способ добавить аннотации к методам расширения, используя C-API?


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

annotation
Значение аннотации для этого параметра.В настоящее время не поддерживается, поскольку PEP 8 требует, чтобы библиотека Python не использовала аннотации.

1 Ответ

0 голосов
/ 08 июня 2018

TL; DR В настоящее время не существует способа сделать это.

Как подписи и расширения C работают вместе?

Теоретически это работает так (дляОбъекты расширения Python C):

  • Если функция C имеет «правильную строку документации», подпись сохраняется в атрибуте __text_signature__.
  • Если вы вызываете help или inspect.signature на таком объекте он анализирует __text_signature__ и пытается создать из него сигнатуру.

Если вы используете аргумент клиника, вам не нужно писать «правильную строку документации» самостоятельно.Строка подписи генерируется на основе комментариев в коде.Однако 2 шага, упомянутые ранее, все еще происходят.Они просто происходят с автоматически генерируемой линией подписи .

. Поэтому встроенные функции Python, такие как sum, имеют __text-signature__ s:

>>> sum.__text_signature__
'($module, iterable, start=0, /)'

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

В чем проблемы с аннотациями?

Есть несколько проблемс аннотациями:

  • Возврат аннотации нарушает контракт "правильной строки документации".Таким образом, __text_signature__ будет пустым, когда вы добавите аннотацию возврата.Это большая проблема, потому что обходной путь обязательно должен был бы переписать ту часть кода CPython C, которая отвечает за перевод строки документации -> __text_signature__!Это не только сложно, но вам также нужно будет предоставить измененную версию CPython, чтобы она работала для людей, использующих ваши функции.

    Например, если вы используете эту «подпись»:

    ultimate(self, question:str='Life, the universe, everything!') -> int
    

    Вы получаете:

    >>> ultimate.__text_signature__ is None
    True
    

    Но если вы удалите обратную аннотацию:

    ultimate(self, question:str='Life, the universe, everything!')
    

    Это даст вам __text_signature__:

    >>> ultimate.__text_signature__
    "(self, question:str='Life, the universe, everything!')"
    
  • Если у вас нет обратной аннотации, она все равно не будет работать, поскольку аннотации явно не поддерживаются (в настоящее время).

    Предполагается, что у вас есть эта подпись:

    ultimate(self, question:str='Life, the universe, everything!')
    

    Это не работает с inspect.signature (сообщение об исключении фактически говорит обо всем):

    >>> import inspect
    >>> inspect.signature(ultimate)
    Traceback (most recent call last):
    ...
        raise ValueError("Annotations are not currently supported")
    ValueError: Annotations are not currently supported
    

    Функция, которая отвечает за синтаксический анализ __text_signature__, равна inspect._signature_fromstr.Теоретически возможно, что вы , может быть могли бы заставить его работать, исправляя его обезьянами (обратные аннотации все равно не сработали бы!).Но, возможно, нет, есть несколько мест, где делаются предположения о __text_signature__, которые могут не работать с аннотациями.

Будет ли PyFunction_SetAnnotations работать?

В комментариях упоминалась эта функция C API.Однако это намеренно не работает с функциями расширения C.Если вы попытаетесь вызвать его с помощью функции расширения C, это вызовет SystemError: bad argument to internal function call.Я проверил это с помощью небольшого «скрипта» Cython Jupyter:

%load_ext cython

%%cython

cdef extern from "Python.h":
    bint PyFunction_SetAnnotations(object func, dict annotations) except -1

cpdef call_PyFunction_SetAnnotations(object func, dict annotations):
    PyFunction_SetAnnotations(func, annotations)

>>> call_PyFunction_SetAnnotations(sum, {})

---------------------------------------------------------------------------
SystemError                               Traceback (most recent call last)
<ipython-input-4-120260516322> in <module>()
----> 1 call_PyFunction_SetAnnotations(sum, {})

SystemError: ..\Objects\funcobject.c:211: bad argument to internal function

Так что это также не работает с функциями расширения C.

Сводка

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

Однако вы всегда можете просто обернуть функцию расширения C функцией Python (просто обертка).Эта оболочка Python может иметь аннотации функций.Это больше обслуживания и немного медленнее, но избавляет вас от хлопот с подписями и расширениями Си.Я не совсем уверен, но если вы используете Cython для переноса кода на C или C ++, он может даже иметь некоторый автоматизированный инструментарий (автоматическое создание оболочек Python).

...