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()
Другие вопросы по тегам