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

Есть ли способ написать строку doctest python для тестирования сценария, предназначенного для запуска из командной строки (терминала), который не загрязняет примеры документации вызовами os.popen?

#!/usr/bin/env python
# filename: add
"""
Example:
>>> import os
>>> os.popen('add -n 1 2').read().strip()
'3'
"""

if __name__ == '__main__':
    from argparse import ArgumentParser
    p = ArgumentParser(description=__doc__.strip())
    p.add_argument('-n',type = int, nargs   = 2, default = 0,help  = 'Numbers to add.')
    p.add_argument('--test',action = 'store_true',help  = 'Test script.')
    a = p.parse_args()
    if a.test:
        import doctest
        doctest.testmod()
    if a.n and len(a.n)==2:
        print a.n[0]+a.n[1]

Запуск doctest.testmod() без использования popen просто приводит к сбою теста, потому что скрипт запускается в оболочке python, а не в оболочке bash (или DOS).

Продвинутый курс по Python на LLNL предлагает помещать скрипты в файлы, которые отделены от модулей .py. Но тогда строки doctest только проверяют модуль без разбора аргументов. И мой подход os.popen() загрязняет документацию по примерам. Есть ли способ лучше?


person hobs    schedule 02.04.2012    source источник
comment
Я что-то упустил или это можно решить, добавив функцию main? Выполните синтаксический анализ аргумента в блоке if __main__, затем вызовите main(parsed_args)   -  person Daenyth    schedule 02.04.2012
comment
К сожалению, это ничего не изменит. Основная функция не является особенной. Выделение некоторых элементов if main в отдельную функцию вообще не меняет поведение doctest. Вы по-прежнему не можете запустить его как команду оболочки, поскольку сценарий предназначен для использования (и задокументирован).   -  person hobs    schedule 12.07.2012


Ответы (3)


Только что нашел что-то похожее на нужный вам ответ: shell-doctest.

person dmitry_romanov    schedule 12.07.2012
comment
Потрясающий. Как раз то, что я искал. Теперь гораздо больше шансов, что python начнет вторгаться в мир сценариев оболочки, и мне не нужно загромождать свой doctext вещами для преобразования сценариев оболочки. - person hobs; 12.07.2012
comment
Выглядит подозрительно только для Python 2. :-( - person hBy2Py; 12.01.2018

doctest предназначен для запуска кода Python, поэтому вам нужно где-то выполнить преобразование. Если вы полны решимости протестировать интерфейс командной строки напрямую через doctest, одна из возможностей — выполнить замену регулярного выражения на __doc__, прежде чем передать его argparse, чтобы удалить оболочку os.popen:

clean = re.sub(r"^>>> os\.popen\('(.*)'\).*", r"% \1", __doc__)
p = ArgumentParser(description=clean, ...)

(Конечно, есть множество более приятных способов сделать это, в зависимости от того, что вы считаете «хорошим»).

Это очистит его для конечного пользователя. Если вы также хотите, чтобы исходный код выглядел чище, вы можете пойти другим путем: поместить примеры командной строки в строку документации и не использовать doctest.testmodule(). Пропустите строку документации через doctest.script_from_examples и обработайте ее, чтобы вставить вызовы os. (Тогда вам нужно будет встроить его во что-то, чтобы вы могли протестировать его с помощью run_docstring_examples.) doctest не волнует, является ли ввод допустимым python, поэтому вы можете сделать следующее:

>>> print doctest.script_from_examples("""
Here is a commandline example I want converted:
>>> add -n 3 4
7
""")
# Here is a commandline example I want converted:
add -n 3 4
# Expected:
## 7

Это по-прежнему будет отображать подсказку python >>> в справке. Если вас это беспокоит, возможно, вам просто придется обрабатывать строку в обоих направлениях.

person alexis    schedule 02.04.2012
comment
Это хороший способ скрыть строку документации, которая проверяет анализатор аргументов, но он не предоставляет никаких примеров (с ожидаемым результатом), когда пользователь запускает add --help из оболочки ОС. Ваше использование sys.argv кажется грубым эквивалентом os.popen в моем коде, и оно выглядит одинаково безобразно, когда строка документации используется для документации и «справки», а не для тестирования doctest. - person hobs; 02.04.2012
comment
Хорошо, если вам действительно нужны документы и тесты, ориентированные на командную строку, см. Новый ответ. - person alexis; 02.04.2012
comment
Вот это да. Довольно сложно. Спасибо, что вытащили меня из тупика. Теперь я понимаю, почему вы не предложили всю эту сложность в своем первом ответе. Возможно, в будущем doctest или argparse будут включать некоторые функции тестирования оболочки. $$$ вместо >>> кто-нибудь? - person hobs; 04.04.2012

Вы также можете самостоятельно загрузить строку документации и выполнить команду, например in а>.

import sys

module = sys.modules[__name__]
docstring = module.__doc__
# search in docstring for certain regex, and check that the following line(s) matches a pattern.
person bryant1410    schedule 21.08.2019
comment
Это очень похоже на создание собственного модуля doctest. - person hobs; 22.08.2019
comment
Да, так и есть. Вы можете заставить его вызывать parse_args, чтобы он работал намного быстрее, чем порождающие процессы. - person bryant1410; 22.08.2019
comment
Да, это бы ускорило дело. Но вы добавили бы много сложности и потенциальных ошибок в свою тестовую инфраструктуру... что меня действительно пугает. - person hobs; 27.08.2019
comment
Я впечатлен! Одно регулярное выражение для меня много. Регулярные выражения привередливы и склонны к неожиданному неправильному поведению. Вам потребовалось несколько проб и ошибок, чтобы заставить это работать правильно для вашего примера. Я был бы удивлен, если бы он надежно работал во всех ваших проектах и ​​строках документации, а тем более во всех остальных. - person hobs; 29.08.2019
comment
Мне действительно не потребовалось много проб и ошибок. Это было довольно просто. Я только что сделал 2 разных PR, потому что первый порождает процессы, которые медленные, а второй вызывает argparse методы. Я считаю, что если вы всегда следуете простому формату, такому как $program-name [args], и вывод продолжается в следующей строке, а затем есть пробел или что-то в этом роде (может быть, какой-то часовой), это довольно просто. Я согласен с тем, что регулярное выражение может быть сложным в некоторых контекстах (например, при анализе файлов CSV), но здесь это кажется простым для одного проекта. - person bryant1410; 29.08.2019
comment
Я признаю, что единственные коварные примеры, которые я могу привести, маловероятны: try my\n$ 1000 program или we signed\n$ million contract - person hobs; 03.09.2019
comment
В моем случае я поставил, что он должен начинаться с $. - person bryant1410; 04.09.2019
comment
Все примеры неудачных тестов начинаются с $. Предыдущий текст находится на строке выше. - person hobs; 05.09.2019
comment
Я имею в виду, что строка начинается с него. - person bryant1410; 06.09.2019
comment
Теперь я понимаю, что вы имеете в виду. Вы можете сделать это только там, предыдущая строка может быть пустой. - person bryant1410; 06.09.2019