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

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

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

    /* blabla [...] */

}

PyDoc_STRVAR(
    foo_doc,
    "Great example functionn"
    "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(...) by foo (тайм-аут, флаги=нет)

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

2 ответов


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

в принципе, я бы предположил, что стандартные модули 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 [...]

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

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

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

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


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

Python сам использует Клиника Аргумент динамически генерировать подписи. Тогда некоторые механики создают __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-extension, предоставляемых этим пакетом:

>>> 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 и вам нужно следовать некоторым правилам:

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

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

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

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

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