如何记录从其他模块导入的类 - 因此没有声明模块的名称?

20
我正在记录的软件包由一组*.py文件组成,大多数文件包含一个类,有几个文件是真正定义了函数的模块。我不需要暴露每个类在哪个模块中,因此我已经在__init__.py文件中添加了适当的from语句。
from base import Base

这样用户就可以使用import pkg命令,而不必指定包含类的模块:

import pkg

class MyBase(pkg.Base):  # instead of pkg.base.Base ...
...

问题在于Sphinx坚持将类记录为pkg.base.Base。我已经尝试在conf.py中设置add_module_names = False。然而,这会导致Sphinx将类显示为简单的Base,而不是pkg.Base。此外,这还破坏了一些*.py文件的文档,这些文件实际上是模块。
如何让Sphinx将类显示为pkg.Base? 如何为每个*.py文件选择性地设置add_module_names指令?

2
不要这样做。Sphinx 正确地告诉用户类在哪里定义,而不是导入的位置。如果您在两个不同的模块中导入Base类,那么Sphinx怎么知道您想使用哪个名称?如果您不希望用户知道定义类的模块,则应将其设置为私有(如果我记得正确的话,它不会显示在生成的文件中)。 - Bakuriu
3
@Bakuriu - 不确定我是否理解您的评论。澄清一下,每个类都在单独的文件中的唯一原因是为了更容易管理源代码控制。使用这些类只需要包名和类名,不需要模块名(这是多余的)。文档应该描述用法而不是定义,您认为呢?如何使模块名私有? - Major Eccles
那问题在哪里呢?只需使用 add_module_names = False 并将文档放置在正确的页面即可。用户将在引用模块 pkg 的页面中看到有一个记录的类 Base,这很好。pkg.module.Base 指定了定义类的模块,而不是您“使用它”的地方(这是一些未定义的东西)。如果您想要获得所需的输出的肮脏解决方案,则可以在 __init__ 中创建一个虚假的子类:class TheClass(pkg.module.TheClass): pass,继承文档。 - Bakuriu
4个回答

15

以下是实现OP所要求的方法:

  1. pkg/__init__.py中添加一个__all__列表:

    from base import Base    # Or use 'from base import *'
    
    __all__ = ["Base"]
    
  2. 在.rst文件中使用.. automodule:: pkg

Sphinx现在将输出文档,其中类名显示为pkg.Base而不是pkg.base.Base


这个问题在于如果 Base 继承自 base 中也定义的基础类,则 Sphinx 继承链接将会中断。 - Matt
2
你的意思是针对Base,"Bases" 是指 pkg.base.ParentClass 而不是 pkg.ParentClass 吗? - mzjn
这个有帮助吗?https://dev59.com/mqfja4cB1Zd3GeqPzKlX - mzjn
1
是的,那就是我的意思。我确实看到了,但是操作所有基类的__module__似乎对我来说太冒险了。特别是因为其中一些是Cython代码,我想知道它是否会破坏自动文档生成器定位源代码进行解析的功能。 - Matt
Sphinx只是简单地输出了"pkg.Base: An alias of pkg.base.Base",并没有对Base类的任何内容进行文档化。 - Ark-kun

1
我希望能提供一种更加通用的方法。
变量__all__是基于dir()填充的。但是子包名称(这里是mypackage)和所有内置属性(以__开头)都被忽略了。
from .mypackage import *

__all__ = []
for v in dir():
    if not v.startswith('__') and v != 'mypackage':
        __all__.append(v)

1

我把我找到的答案整合成了一种可扩展的形式:

my_project/
    __init__.py
    mess.py
  • mess.py:
class MyClass:
    pass

class MyOtherClass(MyClass):
    pass
  • __init__.py:
from .mess import MyClass, MyOtherClass

__all_exports = [MyClass, MyOtherClass]

for e in __all_exports:
    e.__module__ = __name__

__all__ = [e.__name__ for e in __all_exports]

这对我来说似乎运行得相当不错。


当您使用from .mess import *而不是明确命名要导入什么时,您将如何实现这一点? - buhtz
我避免使用 * 导入,所以我甚至没有考虑过这个问题,也不确定什么是最好的解决方案。 - Micael Jarniac

-4
简短回答:你不应该这样做。只需将Sphinx指向代码目录即可。Sphinx会记录代码并显示模块层次结构。最终如何导入模块完全由开发人员决定,而不是文档工具的责任。

20
我不同意。文档应该反映库作者预期的软件包结构。 - Ed Bordin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接