11

我有一个应该有的模块@property,我通过将一个类设置为模块来解决这个问题。我从这个答案中得到了想法:懒惰的模块变量——可以做到吗?

我希望这是可重复且易于使用的,因此我为它制作了一个元类。这就像一个魅力。

问题是当使用 Sphinx 生成文档属性时没有记录。其他所有内容都按预期记录。我不知道如何解决这个问题,也许这是 Sphinx 的问题?

模块:

import sys
import types

class ClassAsModule(type):
    def __new__(cls, name, bases, attrs):
        # Make sure the name of the class is the module name.
        name = attrs.pop('__module__')
        # Create a class.
        cls = type.__new__(cls, name, bases, attrs)
        # Instantiate the class and register it.
        sys.modules[name] = cls = cls(name)
        # Update the dict so dir works properly
        cls.__dict__.update(attrs)

class TestClass(types.ModuleType):
    """TestClass docstring."""
    __metaclass__ = ClassAsModule
    @property
    def some_property(self):
        """Property docstring."""
        pass
    def meth():
        """meth doc"""
        pass

以及用于生成/查看 Sphinx 文档的复制粘贴:

sphinx-apidoc . -o doc --full
sphinx-build doc html
xdg-open html/module.html

最重要的部分是记录类的属性。奖励积分还可以记录原始模块成员。

编辑:该类应记录为它所在的模块。该类以这种方式使用,因此应该以这种方式出现在 Sphinx 中。

所需输出的示例:

Module Foo
    TestClass docstring.

    some_property
        Property docstring.

    meth()
        meth doc

编辑2:我发现了一些可能有助于找到解决方案的东西。foo具有以下内容的常规模块时:

#: Property of foo
prop = 'test'

狮身人面像记录这样的:

foo.prop = 'test'
    Property of foo

如果prop是类的属性,则同样有效。我还没弄清楚为什么它在我的特殊情况下不起作用。

4

2 回答 2

2

这是我的理解。

理论是:让你的类像一个模块一样的变种人这种(有点hacky)的方式让sphinx认为他不需要(解析)模块的属性(因为它是一个类级别的范例)。所以,对于 sphinx 来说,TestClass是一个模块。

首先,要确保罪魁祸首是使类像模块一样运行的代码 - 让我们删除它:

class ClassAsModule(type):
    pass

我们将在文档中看到:

package Package
    script Module

    class package.script.ClassAsModule
        Bases: type

    class package.script.TestClass
        Bases: module

        TestClass docstring.

        meth()
            meth doc

        some_property
            Property docstring.

如您所见,sphinx 毫无问题地读取了该属性。这里没什么特别的。


您的问题的可能解决方案是避免使用@property装饰器并将其替换为调用property类构造函数。例如:

import sys
import types

class ClassAsModule(type):
    def __new__(cls, name, bases, attrs):
        # Make sure the name of the class is the module name.
        name = attrs.pop('__module__')
        # Create a class.
        cls = type.__new__(cls, name, bases, attrs)
        # Instantiate the class and register it.
        sys.modules[name] = cls = cls(name)
        # Update the dict so dir works properly
        cls.__dict__.update(attrs)


class TestClass(types.ModuleType):
    """TestClass docstring."""
    __metaclass__ = ClassAsModule

    def get_some_property(self):
        """Property docstring."""
        pass

    some_property = property(get_some_property)

    def meth(self):
        """meth doc"""
        pass

对于此代码 sphinx 生成:

package Package
    script Module
        TestClass docstring.

            package.script.get_some_property(self)
                Property docstring.

            package.script.meth(self)
                meth doc

答案可能是一派胡言,但我希望它能为您指明正确的方向。

于 2013-05-23T21:37:44.447 回答
0

我发现最好的方法是保持文件内容与编写常规模块一样,然后最后替换胚胎模块sys.modules

"""Module docstring.  """

import sys
import types

def _some_property(self):
    pass
some_property = property(_some_property)
"""Property docstring."""

def meth():
    """meth doc"""
    pass

def _make_class_module(name):
    mod = sys.modules[name]
    cls = type('ClassModule', (types.ModuleType,), mod.__dict__)
    clsmod = cls(name)
    clsmod.__dict__.update(mod.__dict__)
    clsmod.__wrapped__ = mod
    sys.modules[name] = clsmod
_make_class_module(__name__)

文本文档:

mymod Module
************

Module docstring.

mymod.meth()

   meth doc

mymod.some_property = None

   Property docstring.

对于我正在使用的 Sphinx 版本(v1.1.3),看起来您必须显式应用属性构造函数(您不能将其用作装饰器),并且文档字符串必须放在顶部的文件中级别,在创建属性的构造函数调用之后的行上(它不能作为属性获取器内的文档字符串)。不过,源代码仍然相当可读。

于 2013-09-24T16:34:47.850 回答