2016-12-09 13 views
2

Я попытался использовать экземпляр StringIO в doctest в моем классе в программе Python 2.7. Вместо того, чтобы получать какой-либо результат теста, я получаю ответ «Нечего делать».Зачем нужен модуль, нарушающий мой doctest (Python 2.7)

Этот упрощенный тестовый пример демонстрирует ошибку:

#!/usr/bin/env python2.7 
# encoding: utf-8 

class Dummy(object): 
    '''Dummy: demonstrates a doctest problem 

    >>> from StringIO import StringIO 
    ... s = StringIO() 
    ... print("s is created") 
    s is created 
    ''' 

if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 

Ожидаемое поведение: тест пройден.

Наблюдаемое поведение: тест не пройден, с выходом, как это:

% ./src/doctest_fail.py 
********************************************************************** 
File "./src/doctest_fail.py", line 7, in __main__.Dummy 
Failed example: 
    from StringIO import StringIO 
    s = StringIO() 
    print("s is created") 
Expected: 
    s is created 
Got nothing 
********************************************************************** 
1 items had failures: 
    1 of 1 in __main__.Dummy 
***Test Failed*** 1 failures. 

Почему это doctest неисправного? Какое изменение мне нужно сделать, чтобы иметь возможность использовать StringIO-подобные функции (буквальную строку с файловым интерфейсом) в моих доктринах?

+4

Что происходит с '...' вместо '>>>' на нескольких строках non-continuation? – user2357112

+0

Как видно из ответов, проблема действительно в синтаксисе синтаксиса, а не в StringIO. Я удаляю тег StringIO и переписываю вопрос, чтобы не упоминать StringIO. –

ответ

1

[Опираясь на wim-е правильный ответ, но объяснить, почему немного больше, с видом на нижележащих doctest семантики.]

Пример терпит неудачу, потому что он использует синтаксис PS2 (...) вместо PS1 синтаксис (>>>) перед отдельными простыми заявлениями.

Изменение ... в >>>:

#!/usr/bin/env python2.7 
# encoding: utf-8 

class Dummy(object): 
    '''Dummy: demonstrates a doctest problem 

    >>> from StringIO import StringIO 
    >>> s = StringIO() 
    >>> print("s is created") 
    s is created 
    ''' 

if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 

Теперь исправленный пример, переименованный doctest_pass.py, работает без ошибок. Он не производит никакого вывода, а это означает, что все тесты проходят:

% src/doctest_pass.py      

Почему синтаксис >>> правильно? Ссылка на библиотеку Python для doctest, 25.2.3.2. How are Docstring Examples Recognized? должна быть местом, где можно найти ответ, но это не очень понятно об этом синтаксисе.

Doctest просматривает документацию, ища «Примеры». Где он видит строку PS1 >>>, она берет все оттуда до конца строки в качестве примера. Он также добавляет следующие строки, которые начинаются с строки PS2 ... в пример (см.: _EXAMPLE_RE в классе doctest.DocTestParser, строки 584-595). Он берет последующие строки до следующей пустой строки или строки, начинающейся с строки PS1, в качестве Wanted Output.

Doctest компилирует каждый пример, как Python «интерактивное заявление», используя встроенную функцию compile() в exec statement (См: doctest.DocTestRunner.__run(), строки 1314-1315).

«interactive statement» - это список инструкций, заканчивающийся символом новой строки, или Compound Statement. Составной оператор, например. a if или try, «в общем, [... охватывает] несколько строк, хотя в простых воплощениях целая составная инструкция может содержаться в одной строке."Вот несколько строк составного оператора:.

if 1 > 0: 
    print("As expected") 
else: 
    print("Should not happen") 

Список утверждение один или более simple statement с на одной строке, разделенных точкой с запятой

from StringIO import StringIO 
s = StringIO(); print("s is created") 

Итак, doctest на вопрос не удалось из-за него содержащий один простой пример с тремя простыми операторами и разделителями с запятой. Изменение строк PS2 для строк PS1 преуспевает, поскольку оно превращает docstring в последовательность из трех примеров, каждая из которых имеет один простой оператор. Хотя эти три строки работают вместе, чтобы настроить один тест на один элемент функциональности, они не являются одним испытательным прибором. Это три теста, два из которых устанавливают состояние, но на самом деле не тестируют основные функции.

Кстати, вы можете увидеть количество примеров, которые doctest распознает с помощью флага -v. Обратите внимание, что в нем говорится: «3 tests in __main__.Dummy». Можно подумать о трех линиях как одном тестовом блоке, но doctest видит три примера. Первые два примера не имеют ожидаемого результата. Когда в примере выполняется и не генерируется вывод, это считается «проходом».

% src/doctest_pass.py -v 
Trying: 
    from StringIO import StringIO 
Expecting nothing 
ok 
Trying: 
    s = StringIO() 
Expecting nothing 
ok 
Trying: 
    print("s is created") 
Expecting: 
    s is created 
ok 
1 items had no tests: 
    __main__ 
1 items passed all tests: 
    3 tests in __main__.Dummy 
3 tests in 2 items. 
3 passed and 0 failed. 
Test passed. 

В одной док-строке примеры выполняются последовательно. Изменения состояния из каждого примера сохраняются для следующих примеров в одной и той же документации. Таким образом, оператор import определяет имя модуля, оператор присваивания s = использует это имя модуля и определяет имя переменной и т. Д. Документация доктрины, 25.2.3.3. What’s the Execution Context?, наклонно раскрывает это, когда она говорит: «примеры могут свободно использовать ... имена, определенные ранее в выполняемой docstring».

Предыдущее предложение в этом разделе: «Каждый раз, когда доктрина находит проверку docstring для проверки, она использует неглубокую копию глобалов M, так что ... один тест в M не может оставлять за собой крохи, которые случайно позволяют другому тесту работать ", немного вводит в заблуждение. Это правда, что один тест в M не может повлиять на тест в другой докшлинке. Однако в пределах одной докшлинга предыдущий тест, безусловно, оставит за собой крохи, что может повлиять на последующие тесты.

Почему пример в Справочнике по библиотеке Python для doctest, 25.2.3.2. How are Docstring Examples Recognized?, показывает пример с синтаксисом ...? В этом примере показан оператор if, который представляет собой составной оператор на нескольких строках. Вторая и последующая строки отмечены строками PS2.

5

Это синтаксис строки продолжения (...), который сбивает с толку аналитический парсер. Это работает:

#!/usr/bin/env python2.7 
# encoding: utf-8 

class Dummy(object): 
    '''Dummy: demonstrates a doctest problem 

    >>> from StringIO import StringIO 
    >>> s = StringIO() 
    >>> print("s is created") 
    s is created 
    ''' 

if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 
+0

Спасибо! Вы правы, я использовал синтаксис линии продолжения, и переключение на '>>>' синтаксис исправляет его. Вы не делаете следующий шаг, чтобы объяснить, почему мой синтаксис был неправильным с точки зрения семантики 'doctest'. Я напишу свой собственный ответ, чтобы сделать это. –