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

发布于 2024-07-26 18:30:14 字数 1008 浏览 8 评论 0原文

我正在编写 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(...) by foo(timeout, flags=None)

我可以这样做吗? 如何?

I am writing C extensions, and I'd like to make the signature of my methods visible for introspection.

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

Now if (after building it...) I load the module and look at its help:

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

I will get:

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.

I would like to be even more specific and be able to replace foo(...) by foo(timeout, flags=None)

Can I do this? How?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

〆一缕阳光ご 2024-08-02 18:30:14

已经过去 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.
[...]

此函数的文档字符串定义为 此文件

重要的是要认识到这对于 python 来说是不可能的。 3.4 并且您需要遵循一些规则:

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

  • 签名必须位于文档字符串的第一行。

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

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

    <前><代码>foo(a)
    foo(x, a, b)
    --

    叙述性文档

It has been 7 years but you can include the signature for C-extension function and classes.

Python itself uses the Argument Clinic to dynamically generate signatures. Then some mechanics create a __text_signature__ and this can be introspected (for example with help). @MartijnPieters explained this process quite well in this answer.

You may actually get the argument clinic from python and do it in a dynamic fashion but I prefer the manual way: Adding the signature to the docstring:

In your case:

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

I made heavy use of this in my package: iteration_utilities/src. So to demonstrate that it works I use one of the C-extension functions exposed by this package:

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

The docstring for this function is defined this file.

It is important to realize that this isn't possible for python < 3.4 and you need to follow some rules:

  • You need to include --\n\n after the signature definition line.

  • The signature must be in the first line of the docstring.

  • The signature must be valid, i.e. foo(a, b=1, c) fails because it's not possible to define positional arguments after arguments with default.

  • You can only provide one signature. So it doesn't work if you use something like:

    foo(a)
    foo(x, a, b)
    --
    
    Narrative documentation
    
要走就滚别墨迹 2024-08-02 18:30:14

我查找此类问题的常用方法是:“使用源代码”。

基本上,我认为 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 源代码,情况仍然如此。 该签名是在 python 源代码中的 pydoc.py 中生成的:pydoc.py。 从第 1260 行开始的相关摘录:

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

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

My usual approach to finding out about things like this is: "use the source".

Basically, I would presume that the standard modules of python would use such a feature when available. Looking at the source (for example here) should help, but in fact even the standard modules add the prototype after the automatic output. Like this:

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

So as upstream is not using such a feature, I would assume it is not there. :-)

Okay, I just checked current python3k sources and this is still the case. That signature is generated in pydoc.py in the python sources here: pydoc.py. Relevant excerpt starting in line 1260:

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

inspect.isfunction checks if the object the documentation is requested for is a Python function. But C implemented functions are considered builtins, therefore you will always get name(...) as the output.

黑色毁心梦 2024-08-02 18:30:14

如果它有帮助的话,我发现类似下面的东西可以提供类型签名并让 PyC​​harm 理解类型。

对于应该具有以下签名的函数(并且第二个参数是可选的):

def function(paramA: str, paramB: bool = False) -> str:

我将在 PyMethodDef 的 ml_doc 字段中使用以下内容:

"function(paramA, /, paramB=False)\n--\nn"
"function(paramA: str, paramB: bool=False) -> str\n"
":param str paramA: blah blah blah\n"
":param str paramB: blah blah blah\n"
":returns: blah blah blah\n"
":rtype: str\n\n"
"some narrative description of the function"

python 使用“\n--\n\n”之前的所有内容来填充方法上的__text_signature__。 其余部分进入__doc__。 PyCharm 解析 __doc__ 以找出类型。 :rtype: 行末尾的双换行符是 PyCharm 正确处理它所必需的,否则将附加以下描述。

In case it is helpful, I have found that something like the following works to provide a type signature and also let PyCharm understand the typing.

For a function that should have the following signature (and where the second parameter is optional):

def function(paramA: str, paramB: bool = False) -> str:

I would use the following in the ml_doc field of PyMethodDef:

"function(paramA, /, paramB=False)\n--\nn"
"function(paramA: str, paramB: bool=False) -> str\n"
":param str paramA: blah blah blah\n"
":param str paramB: blah blah blah\n"
":returns: blah blah blah\n"
":rtype: str\n\n"
"some narrative description of the function"

Everything before the "\n--\n\n" is used by python to populate __text_signature__ on the method. The rest goes into __doc__. PyCharm parses __doc__ to figure out the types. The double newline at the end of the :rtype: line is necessary for PyCharm to process it correctly, otherwise the following description gets annexed to it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文