python Sphinx "модуль выполняет оператор уровня модуля и может вызвать sys.exit()"

Я пытаюсь использовать 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()

Я думаю, что это связано с "побочными эффектами", когда я импортирую модуль.

Почему это плохо и как я могу это исправить?

0 ответов

Предупреждение Сфинкса - это эффект, вызванный предыдущим parse_args()ошибка. Вarg_parse() функция вызывается, находит нормальную ошибку в аргументах, которые она должна анализировать, и существует.

Недействительные аргументы

Во время синтаксического анализа командной строки parse_args() проверяет различные ошибки, включая неоднозначные параметры, недопустимые типы, недопустимые параметры, неправильное количество позиционных аргументов и т. Д. Когда он обнаруживает такую ​​ошибку, он выходит и печатает ошибку вместе с сообщение об использовании:

Наиболее вероятная причина parse_args()выходит при создании документации Sphinx, потому что на самом деле вы вызываетеsphinx-build или make html. Итак, в вашей оболочке python выполняется следующая подпись:

сфинкс

Синопсис

sphinx-build [параметры] [имена файлов…]

Это означает, что вы, вероятно, не выполняете свой скрипт с аргументами, которые вы закодировали. ArgumentParserтребовать. Либо бегиsphinx-build или make html включая аргументы командной строки arg_parse() требует вArgumentParser(), или не звони 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.py поддерживает argsв качестве переменной на уровне модуля, чтобы она была максимально похожа на учебный пример argparse. Специальные расширения Sphinx дляargparseможно найти в этой ветке.

2. Использование пространства имен по умолчанию дляargsможет быть удобно, он включен в качестве предложения. (Ожидаемые значения по умолчанию могут помочь в тестах при импорте модулей).

3. Тестирование на__main __ или sphinx-build может не понадобиться в зависимости от того, 3 if тесты включены только для добавления контекста к вопросу.

4. ИспользованиеDEFAULT_ARGS показывает, как использовать parse_args() без чтения из sys.argv, а также как можно бегать sphinx-build дозирование использования if __name __ == "__main __": (если вам это удобно по какой-либо причине)...

5. Имя и путь кsphinx-build сценарий может отличаться в зависимости от операционной системы.

Последнее замечание: если вам нравится писать модули, которые самоинициализируют свои переменные (как я), обратите особое внимание на запуск parse_args() перед импортом модулей, которые могут иметь зависящие от него переменные.

6.1. Подробнее о модулях

Модуль может содержать исполняемые операторы, а также определения функций. Эти операторы предназначены для инициализации модуля. Они выполняются только в первый раз, когда имя модуля встречается в операторе импорта. (Они также запускаются, если файл выполняется как сценарий.)

У меня тоже была эта проблема. Мое решение заключалось в том, чтобы добавить в сценарий перед кодом argparse этот оператор if:

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

В моем случае у меня был подмодуль с импортом сторонней библиотеки, и эта библиотека была источником проблемы (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

где a а также b имел import dнить в них. А такжеimport pyautogui был в FOO.d.bar подмодуль.

Другие вопросы по тегам