Doctests: как подавить / игнорировать вывод?
Тестирование следующего (бессмысленного) модуля Python завершается неудачно:
"""
>>> L = []
>>> if True:
... append_to(L) # XXX
>>> L
[1]
"""
def append_to(L):
L.append(1)
class A(object):
pass
return A()
import doctest; doctest.testmod()
Это потому, что вывод после строки, отмеченной XXX, <__main__.A object at ...>
(который возвращается append_to
). Конечно, я мог бы поместить этот вывод непосредственно после строки, помеченной XXX, но в моем случае это отвлекло бы читателя от того, что будет фактически проверено, а именно от побочного эффекта функции append_to
, Итак, как я могу подавить этот вывод или как я могу его игнорировать. Я попробовал это с:
"""
>>> L = []
>>> if True:
... append_to(L) # doctest: +ELLIPSIS
...
>>> L
[1]
"""
def append_to(L):
L.append(1)
class A(object):
pass
return A()
import doctest; doctest.testmod()
Тем не менее, это дает ValueError: line 4 of the docstring for __main__ has inconsistent leading whitespace: ' ...'
,
Чего я не хочу делать, так это менять линию append_to(L)
что-то вроде _ = append_to(L)
который подавил бы вывод, потому что doctest предназначен для целей документирования и чтобы показать читателю, как предполагается использовать модуль.
(В случае документирования, append_to
должен использоваться как оператор, а не как функция. Пишу _ = append_to(L)
отклонил бы читателя от этого.)
3 ответа
переписать: это на самом деле работает сейчас; Я понял, что "doctest", который я написал ранее, на самом деле не анализировался как строка документации модуля, поэтому тест не проходил: он просто не запускался.
Я удостоверился, чтобы перепроверить это.
__doc__ = """
>>> L = []
>>> if True:
... append_to(L) # doctest: +IGNORE_RESULT
>>> L
[1]
""".replace('+IGNORE_RESULT', '+ELLIPSIS\n<...>')
def append_to(L):
L.append(1)
class A(object):
pass
return A()
Я не уверен, является ли это более читабельным или нет. Обратите внимание, что в этом нет ничего особенного <...>
: он будет работать только в том случае, если фактическое возвращаемое значение имеет такую форму, как в этом случае (т.е. <module.A object at 0x...>
). Опция ELLIPSIS делает ...
msgstr "соответствовать любой подстроке в фактическом выводе" ¹. Так что я не думаю, что есть способ заставить его соответствовать всему результату.
обновление: чтобы сделать это "правильным" способом, похоже, что вы хотите позвонить doctest.register_optionflag('IGNORE_RESULT')
подкласс doctest.OptionChecker
и организуем, чтобы экземпляр этого подкласса использовался документом. Предположительно это означает, что запуск вашего doctest через $ python -m doctest your_module.py
это не вариант.
Я оказался в этом вопросе, потому что мне нужно поведение +IGNORE_RESULT, но для запуска тестов документации в документации sphinx. В
replace
ответ, отправленный @intuited, не работает в этом случае, и не было кода для упомянутого «правильного» решения. Поэтому выкладываю то, что у меня получилось.
В качестве прямого ответа на вопрос решение было бы следующим:
__doc__ = """
>>> L = []
>>> if True:
... append_to(L) # doctest: +IGNORE_RESULT
>>> L
[1]
"""
def append_to(L):
L.append(1)
class A(object):
pass
return A()
if __name__ == "__main__":
import doctest
IGNORE_RESULT = doctest.register_optionflag('IGNORE_RESULT')
OutputChecker = doctest.OutputChecker
class CustomOutputChecker(OutputChecker):
def check_output(self, want, got, optionflags):
if IGNORE_RESULT & optionflags:
return True
return OutputChecker.check_output(self, want, got, optionflags)
doctest.OutputChecker = CustomOutputChecker
doctest.testmod()
Для моей конкретной потребности в документации по sphinx для тестирования документации добавьте в
conf.py
файл:
import doctest
IGNORE_RESULT = doctest.register_optionflag('IGNORE_RESULT')
OutputChecker = doctest.OutputChecker
class CustomOutputChecker(OutputChecker):
def check_output(self, want, got, optionflags):
if IGNORE_RESULT & optionflags:
return True
return OutputChecker.check_output(self, want, got, optionflags)
doctest.OutputChecker = CustomOutputChecker
Затем проверьте с помощью такой команды, как:
sphinx-build -M doctest source_dir build_dir source_dir/file.rst
Пожалуйста, попробуйте дать полностью автономный, работающий код; даже когда вы демонстрируете проблему, код должен запускаться сам по себе, чтобы воспроизвести проблему, поэтому решения могут скопировать код непосредственно для демонстрации ответа.
Я не знаю чистого решения для этого, и я достиг этого раньше; это кажется побочным эффектом нечетких (более грубо: небрежных) тестовых определений doctests. Обходной путь - помнить, что вы можете определять функции в тестах документов, чтобы вы могли содержать весь тест как отдельную функцию, а не как отдельные операторы.
def append_to(l):
"""
>>> L = []
>>> def test():
... if True:
... append_to(L) # XXX
>>> test()
>>> L
[1]
>>> def test():
... L = []
... if True:
... append_to(L) # XXX
... return L
>>> test()
[1]
"""
l.append(1)
return object()
if __name__ == "__main__":
import doctest
doctest.testmod()