Расширение Python C: подписи методов для документации?

Я пишу 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)

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

Ответ 1

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

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

[email protected]:~$ 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(...) в качестве вывода.

Ответ 2

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

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

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

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

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.
[...]

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

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

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

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

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

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

    foo(a)
    foo(x, a, b)
    --
    
    Narrative documentation