Python C扩展:文档的方法签名?

Python C extension: method signatures for documentation?

我正在编写C扩展,我想让我的方法的签名可见以便进行自省。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static PyObject* foo(PyObject *self, PyObject *args) {

    /* blabla [...] */

}

PyDoc_STRVAR(
    foo_doc,
   "Great example function
"

   "Arguments: (timeout, flags=None)
"

   "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");
}

现在,如果(在构建它之后…)我加载模块并查看其帮助:

1
2
>>> import _myexample
>>> help(_myexample)

我会得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
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(超时,标志=none)替换foo(…)。

我能做这个吗?怎么用?


它已经有7年了,但是您可以包含C扩展函数和类的签名。

python本身使用参数clinic动态生成签名。然后,一些机制创建一个__text_signature__,这可以进行内省(例如,help)。@马蒂·皮耶特在这个答案中很好地解释了这个过程。

实际上,您可以从python获得参数clinic,并以动态方式进行,但我更喜欢手动方式:向docstring添加签名:

在您的情况下:

1
2
3
4
5
6
7
8
9
10
11
12
13
PyDoc_STRVAR(
    foo_doc,
   "foo(timeout, flags=None, /)
"

   "--
"

   "
"

   "Great example function
"

   "Arguments: (timeout, flags=None)
"

   "Doc blahblah doc doc doc.");

我在包装中大量使用了这个:iteration_utilities/src。为了证明它是有效的,我使用了这个包公开的C扩展函数之一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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是不可能的,您需要遵循一些规则:

  • 您需要在签名定义行之后包含--

  • 签名必须在docstring的第一行。

  • 签名必须有效,即foo(a, b=1, c)失败,因为不能在默认参数之后定义位置参数。

  • 您只能提供一个签名。因此,如果你使用类似的东西,它就不起作用了:

    1
    2
    3
    4
    5
    foo(a)
    foo(x, a, b)
    --

    Narrative documentation


我发现这类事情的通常方法是:"使用源代码"。

基本上,我假设Python的标准模块在可用时会使用这种特性。查看源代码(例如这里)应该会有所帮助,但实际上,即使是标准模块也会在自动输出之后添加原型。这样地:

1
2
3
4
5
6
7
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来源,情况仍然如此。该签名在这里的python源中的pydoc.py中生成:pydoc.py。从第1260行开始的相关摘录:

1
2
3
4
5
        if inspect.isfunction(object):
            args, varargs, varkw, defaults = inspect.getargspec(object)
            ...
        else:
            argspec = '(...)'

inspect.is function检查文档请求的对象是否是python函数。但是C实现的函数被认为是内置的,因此您将始终得到name(...)作为输出。