Расширение Python C: метод подписи для документации? - PullRequest
12 голосов
/ 09 июля 2009

Я пишу расширения C, и я хотел бы сделать сигнатуру моих методов видимой для самоанализа.

static PyObject* foo(PyObject *self, PyObject *args) {

    /* blabla [...] */

}

PyDoc_STRVAR(
    foo_doc,
    "Great example function\n"
    "Arguments: (timeout, flags=None)\n"
    "Doc blahblah doc doc doc.");

static PyMethodDef methods[] = {
    {"foo", foo, METH_VARARGS, foo_doc},
    {NULL},
};

PyMODINIT_FUNC init_myexample(void) {
    (void) Py_InitModule3("_myexample", methods, "a simple example module");
}

Теперь, если (после сборки ...) я загружаю модуль и смотрю на его помощь:

>>> import _myexample
>>> help(_myexample)

Я получу:

Help on module _myexample:

NAME
    _myexample - a simple example module

FILE
    /path/to/module/_myexample.so

FUNCTIONS
    foo(...)
        Great example function
        Arguments: (timeout, flags=None)
        Doc blahblah doc doc doc.

Я хотел бы быть еще более конкретным и иметь возможность заменить foo (...) на foo (timeout, flags = None)

Могу ли я сделать это? Как?

Ответы [ 2 ]

6 голосов
/ 20 декабря 2016

Прошло 7 лет , но вы можете включить сигнатуру для функции расширения C и классов .

Сам Python использует Argument Clinic для динамического генерирования подписей. Затем некоторые механики создают __text_signature__, и это может быть самоанализ (например, с help). @MartijnPieters довольно хорошо объяснил этот процесс в этом ответе .

На самом деле вы можете получить клинику аргументов от python и делать это динамически, но я предпочитаю ручной способ: добавление подписи в строку документации:

В вашем случае:

PyDoc_STRVAR(
    foo_doc,
    "foo(timeout, flags=None, /)\n"
    "--\n"
    "\n"
    "Great example function\n"
    "Arguments: (timeout, flags=None)\n"
    "Doc blahblah doc doc doc.");

Я интенсивно использовал это в своей посылке: iteration_utilities/src. Итак, чтобы продемонстрировать, что это работает, я использую одну из функций расширения C, предоставляемых этим пакетом:

>>> from iteration_utilities import minmax
>>> help(minmax)
Help on built-in function minmax in module iteration_utilities._cfuncs:

minmax(iterable, /, key, default)
    Computes the minimum and maximum values in one-pass using only
    ``1.5*len(iterable)`` comparisons. Recipe based on the snippet
    of Raymond Hettinger ([0]_) but significantly modified.

    Parameters
    ----------
    iterable : iterable
        The `iterable` for which to calculate the minimum and maximum.
[...]

Строка документации для этой функции определена этот файл .

Важно понимать, что это невозможно для питона <3.4 </strong>, и вам нужно следовать некоторым правилам:

  • Вам необходимо включить --\n\n после строки определения подписи.

  • Подпись должна быть в первой строке строки документа.

  • Подпись должна быть действительной, т. Е. foo(a, b=1, c) терпит неудачу, потому что невозможно определить позиционные аргументы после аргументов по умолчанию.

  • Вы можете предоставить только одну подпись. Так что это не сработает, если вы используете что-то вроде:

    foo(a)
    foo(x, a, b)
    --
    
    Narrative documentation
    
6 голосов
/ 09 июля 2009

Мой обычный подход к выяснению таких вещей: «использовать источник».

По сути, я бы предположил, что стандартные модули python будут использовать такую ​​возможность, когда она будет доступна. Просмотр источника (, например, здесь ) должен помочь, но на самом деле даже стандартные модули добавляют прототип после автоматического вывода. Как это:

torsten@pulsar:~$ python2.6
>>> import fcntl
>>> help(fcntl.flock)
flock(...)
    flock(fd, operation)

    Perform the lock operation op on file descriptor fd.  See the Unix [...]

Так как апстрим не использует такую ​​функцию, я бы предположил, что ее там нет. : -)

Хорошо, я только что проверил текущие исходники python3k, и это все еще так. Эта подпись генерируется в pydoc.py в источниках Python здесь: pydoc.py . Соответствующая выдержка, начинающаяся со строки 1260:

        if inspect.isfunction(object):
            args, varargs, varkw, defaults = inspect.getargspec(object)
            ...
        else:
            argspec = '(...)'

inspect.isfunction проверяет, является ли объект, для которого запрашивается документация, функцией Python. Но реализованные на C функции считаются встроенными, поэтому вы всегда получите name(...) в качестве вывода.

...