【问题标题】:python Sphinx "the module executes module level statement and it might call sys.exit()."python Sphinx“模块执行模块级语句,它可能调用 sys.exit()。”
【发布时间】:2016-04-06 19:46:29
【问题描述】:

我尝试使用 Sphinx 来记录我的代码。但我看到了错误:

模块执行模块级语句,它可能会调用 sys.exit()。

我发现这个错误与代码有关:

import argparse
# parse command line arguments
parser = argparse.ArgumentParser(description='AWS VPN status checker.')
parser.add_argument('account', type=str, help='AWS account name.')
parser.add_argument('region', type=str, help='region of VPN tunnel.')
parser.add_argument('ipaddress', type=str, help='Tunnel IP address.')
parser.add_argument("-d", "--debug", help="debug", action="store_true")
args = parser.parse_args()

我认为这与我导入模块时的“副作用”有关。

为什么不好,我该如何解决?

【问题讨论】:

  • “为什么不好” - 因为 Sphinx 导入您的代码来分析它,如果有代码在模块的顶层运行,它可以根据消息,在 Sphinx 完成之前退出。 “我该如何解决这个问题?” - 不要在顶层运行代码。将其移动到一个函数中并添加if __name__ == '__main__' 来调用它(参见stackoverflow.com/q/419163/3001761)。

标签: python python-sphinx argparse


【解决方案1】:

Sphinx 警告是一种效果,由前面的 parse_args() 错误引起。 arg_parse() 函数被调用,在它预期解析的参数中发现一个正常错误,并且存在。

Invalid arguments

在解析命令行时,parse_args()会检查各种错误,包括不明确的选项、无效的类型、无效的选项、错误的位置参数个数等。当遇到此类错误时,它会退出并打印错误以及使用消息:

parse_args() 退出在生成 Sphinx 文档时的最可能原因是因为您真正调用的是 sphinx-buildmake html。因此,在您的 python shell 上执行的是以下签名:

sphinx-build

概要

sphinx-build [选项] [文件名...]

这意味着您可能没有使用您将 ArgumentParser 编码为要求的参数执行脚本。运行 sphinx-buildmake html 包括命令行参数 arg_parse() 需要 inArgumentParser(),或者在生成文档时不要调用 arg_parse()

如何解决这个问题?

一种可能的方法如下:

entry_script.py:

from sys import argv
from pathlib import Path

import cmd_line_module

# Checking what the command line contains can be useful.
print(argv)

EXAMPLE_ARGS = ['-i', '../in_dir_test', '-o', 'out_dir_test']

# Script.
if __name__ == "__main__":

    # default Namespace
    print(cmd_line_params.args)

    # command-line
    cmd_line_module.set_args()
    print(cmd_line_params.args)

    # test case
    cmd_line_module.set_args(EXAMPLE_ARGS)
    print(cmd_line_params.args)

# Sphinx-build or make.
elif Path(argv[0]).name == "sphinx-build" or Path(argv[0]).name == "build.py":

    cmd_line_module.set_args(EXAMPLE_ARGS)

# Module only.
else:
    cmd_line_module.set_args(EXAMPLE_ARGS)

cmd_line_module.py:

import argparse


_DEFAULT = argparse.Namespace(in_dir=None, out_dir=None)
args = _DEFAULT


def command_line_args():

    parser = argparse.ArgumentParser(prog='entry_script', description='Does magic :) .')
    parser.add_argument("-i", "--in_dir", help="Input directory/file. Use absolute or relative path.")
    parser.add_argument("-o", "--out_dir", help="Output directory. Use absolute or relative path.")

    return parser


def set_args(cmd_line=None):

    parser = command_line_args()
    global args
    args = parser.parse_args(cmd_line)

关于解决方案的一些说明可能对读者有用:

1. cmd_line_module.pyargs 维护为模块级别的变量,以尽可能类似于argparse tutorial 示例。 argparse 的特定 Sphinx 扩展可以在 on this thread 找到。

2.args 使用默认的Namespace 可能会很方便,这是作为建议提供的。 (预期的默认值可以帮助测试导入模块)。

3. 可能不需要对__main __sphinx-build 进行测试,这取决于,包含3 个if 测试只是为了为问题添加上下文。

4. 使用DEFAULT_ARGS 展示了如何在不读取sys.argv 的情况下使用parse_args(),以及如何运行sphinx-build 分配使用if __name __ == "__main __":(如果你发现无论出于何种原因都很方便)...

5.sphinx-build 脚本的名称和路径可能因操作系统而异。

最后说明:如果您喜欢编写自初始化其变量的模块(如我),请特别注意在导入可能具有依赖于它的变量的模块之前运行 parse_args()

6.1。 More on Modules

模块可以包含可执行语句以及函数定义。这些语句旨在初始化模块。它们仅在 import 语句中第一次遇到模块名称时执行。 (如果文件作为脚本执行,它们也会运行。)

【讨论】:

    【解决方案2】:

    就我而言,我有一个带有 3rd 方库导入的子模块,而该库是问题的根源 (pyautogui)。 但我花了几个小时试图弄清楚,因为错误是这样的:

    FOO.a import error the module executes module level statement and it might call sys.exit()
    FOO.b import error the module executes module level statement and it might call sys.exit()
    FOO.d.foo import error the module executes module level statement and it might call sys.exit()
    FOO.d.bar import error the module executes module level statement and it might call sys.exit()
    FOO.d.baz import error the module executes module level statement and it might call sys.exit()
    FOO.d import error the module executes module level statement and it might call sys.exit()
    FOO import error the module executes module level statement and it might call sys.exit()
    

    虽然我有类似这样的包结构:

    FOO
    ├── a
    ├── b
    │   ├── ci
    |   └── __init__.py|
    └── d
        ├── foo
        ├── bar
        ├── baz
        └── __init__.py
    

    ab 在其中有 import d 字符串。而import pyautoguiFOO.d.bar 子模块中。

    【讨论】:

    • 我摆脱了导致问题的 3rd 方库。
    【解决方案3】:

    我也有这个问题。我的解决方案是在 argparse 代码之前添加这个 if 语句:

    if __name__ == "__main__":
       parser = argparse.ArgumentParser(description="some description", formatter_class=RawTextHelpFormatter)
       parser.add_argument(....)
       args = parser.parse_args()
       ......
    

    【讨论】:

      猜你喜欢
      • 2012-06-15
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      • 2018-10-23
      • 1970-01-01
      相关资源
      最近更新 更多