重构模块并保持向后兼容性,包括 intersphinx
Refactoring a module and keeping backward compatibility, including for intersphinx
给定一个 python 包 pack
提供 pack.foo.Bar
class:
pack/
__init__.py # empty
foo.py
# content of foo.py
"""
This module does stuff using the :class:`pack.foo.Bar` class.
"""
class Bar(object):
pass
# much more code here
我想将 pack.foo
模块重构为一个包,以便将 Bar
class 移动到 pack/foo/bar.py
文件中。为了保持向后兼容性,我可以把这个放到 pack/foo/__init__.py
文件中:
"""
This module does stuff using the :class:`pack.foo.Bar` class.
"""
from pack.foo.bar import Bar
__all__ = ['Bar']
API 的用户仍然可以使用 from pack.foo import Bar
。
仍然存在一个问题:使用 sphinx 时的引用。 sphinx解析pack/foo/__init__.py
中的docstring时,找不到目标:
WARNING: py:class reference target not found pack.foo.bar.Bar
这会破坏用户在使用 intersphinx 扩展时制作的文档。
重构包结构并仍然保持完全向后兼容性(包括 sphinx 对象清单)的正确方法是什么?
这是我自己的一些发现的答案。
在这种情况下没有灵丹妙药。
首先,sphinx-apidoc 生成的代码文档将具有从文件布局推断出的模块布局。这意味着 pack/foo.py
中定义的 Bar
class 将被记录为 pack.foo.Bar
,无论 pack/__init__.py
.
中发生什么导入处理
其次,autodoc extension仍然可以使用。 Autodoc 只是尝试正常导入文档符号,因为它们在重组文本中定义。这样,您可以使用
为 Bar
class 生成 HTML 文档
.. autoclass:: pack.Bar
:members:
不过有一个问题。任何记录的符号(以及它们的每个依赖项,传递性)必须与旨在记录的完全相同的命名空间一起使用。考虑我们示例的变体,提供额外的 class Baz
:
pack/
__init__.py
# content of __init__.py
from pack.foo.bar import Bar, Baz
__all__ = ['Bar', 'Baz']
foo.py
# content of foo.py
"""
This module does stuff using the :class:`pack.foo.Bar` class.
"""
class Bar(object):
pass
class Baz(Bar): # Here, sphinx thinks that Baz inherits from
pass # pack.foo.Bar because Bar.__module__ is
# pack.foo in this context.
Sphinx 将无法导入 pack.foo.Bar
,因为由于 pack/__init__.py
.
的内容,它只能作为 pack.Bar
导入
为了完成这项工作,必须找到一种方法,在 API 的代码本身中只使用包的 API 提供的精确导入布局。例如,在我们的示例中,这可以通过在单独的文件中定义 Bar
和 Baz
class 来实现。祝你好运,小心循环导入!
给定一个 python 包 pack
提供 pack.foo.Bar
class:
pack/
__init__.py # empty
foo.py
# content of foo.py
"""
This module does stuff using the :class:`pack.foo.Bar` class.
"""
class Bar(object):
pass
# much more code here
我想将 pack.foo
模块重构为一个包,以便将 Bar
class 移动到 pack/foo/bar.py
文件中。为了保持向后兼容性,我可以把这个放到 pack/foo/__init__.py
文件中:
"""
This module does stuff using the :class:`pack.foo.Bar` class.
"""
from pack.foo.bar import Bar
__all__ = ['Bar']
API 的用户仍然可以使用 from pack.foo import Bar
。
仍然存在一个问题:使用 sphinx 时的引用。 sphinx解析pack/foo/__init__.py
中的docstring时,找不到目标:
WARNING: py:class reference target not found pack.foo.bar.Bar
这会破坏用户在使用 intersphinx 扩展时制作的文档。
重构包结构并仍然保持完全向后兼容性(包括 sphinx 对象清单)的正确方法是什么?
这是我自己的一些发现的答案。
在这种情况下没有灵丹妙药。
首先,sphinx-apidoc 生成的代码文档将具有从文件布局推断出的模块布局。这意味着 pack/foo.py
中定义的 Bar
class 将被记录为 pack.foo.Bar
,无论 pack/__init__.py
.
其次,autodoc extension仍然可以使用。 Autodoc 只是尝试正常导入文档符号,因为它们在重组文本中定义。这样,您可以使用
为Bar
class 生成 HTML 文档
.. autoclass:: pack.Bar
:members:
不过有一个问题。任何记录的符号(以及它们的每个依赖项,传递性)必须与旨在记录的完全相同的命名空间一起使用。考虑我们示例的变体,提供额外的 class Baz
:
pack/
__init__.py
# content of __init__.py
from pack.foo.bar import Bar, Baz
__all__ = ['Bar', 'Baz']
foo.py
# content of foo.py
"""
This module does stuff using the :class:`pack.foo.Bar` class.
"""
class Bar(object):
pass
class Baz(Bar): # Here, sphinx thinks that Baz inherits from
pass # pack.foo.Bar because Bar.__module__ is
# pack.foo in this context.
Sphinx 将无法导入 pack.foo.Bar
,因为由于 pack/__init__.py
.
pack.Bar
导入
为了完成这项工作,必须找到一种方法,在 API 的代码本身中只使用包的 API 提供的精确导入布局。例如,在我们的示例中,这可以通过在单独的文件中定义 Bar
和 Baz
class 来实现。祝你好运,小心循环导入!