Sphinx autodoc 使用 mock 卡在 random.choice()

Sphinx autodoc gets stuck on random.choice() using mock

这是我最初在 post 中提出的问题的延续:random.choice error due to np.linspace and np.logspace

在这个问题中,我已将问题简化到最低限度,并且能够重现 Sphinx 在 random.choice() 函数上挂起的问题。

这里是文件randor_test.py中的Python代码;它在 PyCharm:

中运行
import random
import numpy as np

def rand_test(svr_C, svr_gamma):
    """This is test docstring

    #. item one
    #. item two
    """
    ml_params = {'C': random.choice(svr_C), 'gamma': random.choice(svr_gamma)}
    return ml_params

svr_C = list(np.linspace(50, 300, 10))
svr_gamma = list(np.logspace(-4, -2, 3))

rand_result = rand_test(svr_C, svr_gamma)

for i in rand_result:
    print(i, rand_result[i])

我设置了 Sphinx 目录并遵循了 post 中的所有说明:Getting Started with Sphinx...

在 运行 make html 之后我收到以下错误:

WARNING: autodoc: failed to import module 'randor_test'; the following exception was raised:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/sphinx/ext/autodoc/importer.py", line 32, in import_module
    return importlib.import_module(modname)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/kellihed/PAC_projects/basic_0629/src/randor_test.py", line 19, in <module>
    rand_result = rand_test(svr_C, svr_gamma)
  File "/Users/kellihed/PAC_projects/basic_0629/src/randor_test.py", line 10, in rand_test
    ml_params = {'C': random.choice(svr_C), 'gamma': random.choice(svr_gamma)}
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/random.py", line 261, in choice
    raise IndexError('Cannot choose from an empty sequence') from None
IndexError: Cannot choose from an empty sequence

我的文件结构遵循建议的布局:

|--docs
|  |
|  |--Makefile
|  |--build
|  |  |
|  |  |--doctrees
|  |  |--html
|  |  |--_static
|  |  |--genindex.html
|  |  |--index.html 
|  |  |--modules.html
|  |--make.bat
|  |--source
|     |
|     |--_static
|     |--_templates
|     |--conf.py
|     |--index.rst
|     |--modules.rst
|     |--randor_test.rst
|
|--src
|  |__pychache__
|  |  |
|  |  |--randor_test.cpython-37.pyc
|  |
|  |--randor_test.py

我在 conf.py 中有以下忽略导入语句:

autodoc_mock_imports = ["random", "numpy"]

下面是我的 conf.py 文件:

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join('..', '..', 'src')))


# -- Project information -----------------------------------------------------

project = 'random_test'
copyright = '2020, DK'
author = 'DK'

# The full version, including alpha/beta/rc tags
release = '0.1'


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc']
autodoc_mock_imports = ["random", "numpy"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []


# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

您拥有在 Sphinx 导入时始终执行的模块级代码 randor_testsvr_C = list(np.linspace(50, 300, 10))不会直接报错,但结果是svr_C是一个空列表。

执行random.choice(svr_C)时出现错误。我认为 autodoc_mock_imports 中的 random 没有任何区别(它是一个始终可用的内置标准模块)。我认为最好的解决方案是将模块级代码放在 if __name__ == '__main__' 块中。

另见 Does sphinx run my code on executing 'make html'?

如果你不使用 autodoc_mock_imports = ["numpy"] 它会工作而不会报错:

那么为什么 autodoc_mock_imports = ["numpy"] 会导致错误呢?因为使用 mock 会导致 import numpy as np 提供虽然可调用的签名是模型,所以它们的 returns 将是空的。 例如:

import numpy as np
type(np)  # using mock in conf.py
<class 'sphinx.ext.autodoc.mock._MockModule'>
import numpy as np
type(np)  # not using mock in conf.py
<class 'module'>

引用文档:

autodoc_mock_imports This value contains a list of modules to be mocked up. This is useful when some external dependencies are not met at build time and break the building process. You may only specify the root package of the dependencies themselves and omit the sub-modules:

如果您的外部依赖项(换句话说,从您自己的库外部导入)在调用 Sphinx 时在构建时工作正常,则您不必使用 mock。如果您没有使用模拟,您的简短示例将会奏效。 除了使用 __main__ 之外,另一个常见的选择是不通过将变量封装在方法或函数中来在模块级别初始化变量。

6.1. More on Modules A module can contain executable statements as well as function definitions. These statements are intended to initialize the module. They are executed only the first time the module name is encountered in an import statement. (They are also run if the file is executed as a script.)