Как включить специальные символы (tab, newline) в строку результата Python doctest?

учитывая следующий скрипт python:

# dedupe.py
import re

def dedupe_whitespace(s,spacechars='t '):
    """Merge repeated whitespace characters.
    Example:
    >>> dedupe_whitespace(r"GreenttGround")  # doctest: +REPORT_NDIFF
    'GreentGround'
    """
    for w in spacechars:
        s = re.sub(r"("+w+"+)", w, s)
    return s

функция работает по назначению в интерпретаторе python:

$ python
>>> import dedupe
>>> dedupe.dedupe_whitespace('PurplettHaze')
'PurpletHaze'
>>> print dedupe.dedupe_whitespace('BluettSky')
Blue    Sky

однако пример doctest не выполняется, поскольку символы табуляции преобразуются в пробелы перед сравнением с результирующей строкой:

>>> import doctest, dedupe
>>> doctest.testmod(dedupe)

дает

Failed example:
    dedupe_whitespace(r"Green           Ground")  #doctest: +REPORT_NDIFF
Differences (ndiff with -expected +actual):
    - 'Green  Ground'
    ?       -
    + 'Green Ground'

как я могу кодировать символы табуляции в строке doctest heredoc, чтобы сравнение результатов теста выполнялось соответствующим образом?

5 ответов


Я получил это для работы с использованием буквальной Строковой нотации для docstring:

def join_with_tab(iterable):
    r"""
    >>> join_with_tab(['1', '2'])
    '1\t2'
    """

    return '\t'.join(iterable)

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

это сырая нотация строки heredoc (r"""), который сделал трюк:

# filename: dedupe.py
import re,doctest
def dedupe_whitespace(s,spacechars='\t '):
    r"""Merge repeated whitespace characters.
    Example:
    >>> dedupe_whitespace('Black\t\tGround')  #doctest: +REPORT_NDIFF
    'Black\tGround'
    """
    for w in spacechars:
        s = re.sub(r"("+w+"+)", w, s)
    return s

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

необходимо указать NORMALIZE_WHITESPACE. или, альтернативно, захватите выход и сравните его с ожидаемым значением:

def dedupe_whitespace(s,spacechars='\t '):
    """Merge repeated whitespace characters.
    Example:
    >>> output = dedupe_whitespace(r"Black\t\tGround")  #doctest: +REPORT_NDIFF
    >>> output == 'Black\tGround'
    True
    """

С doctest раздел документации как распознаются примеры Docstring?:

все символы жестких вкладок расширяются до пробелов, используя вкладку 8 столбцов останавливает. Вкладки в выходных данных, генерируемых тестируемым кодом, не изменяются. Потому что любые жесткие вкладки в выборке are расширены, это означает, что если вывод кода включает жесткие вкладки, единственный способ doctest может пройти, если NORMALIZE_WHITESPACE опция или директива действует. Альтернативно, тест может быть переписано для записи вывода и сравнения его с ожидаемым значением как часть теста. Эта обработка вкладок в источнике была получена путем проб и ошибок, и оказался наименее подвержен ошибкам способ обращения их. Можно использовать другой алгоритм обработка вкладок путем написания пользовательского DocTestParser класса.

Edit: моя ошибка, я понял документы наоборот. Вкладки расширяются до 8 пробелов в обоих строковых аргументах, переданных в dedupe_whitespace и строковый литерал сравнивается в следующей строке, поэтому output содержит:

"Black Ground"

и сравнивается с:

"Black        Ground"

Я не могу найти способ преодолеть это ограничение без написания собственного DocTestParser или тестирование дедуплицированных пробелов вместо вкладок.


TL; DR: избежать обратной косой черты, т. е. использовать \n или \t вместо \n или \t в противном случае немодифицированной строки;

вы, вероятно, не хотите, чтобы ваши docstrings raw, так как тогда вы не сможете использовать любые строки Python, включая те, которые вы можете захотеть.

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


это в основном ответ YatharhROCK, но немного более явный. Вы можете использовать raw strings или двойное преобразование. Но почему?

вам нужен строковый литерал, содержащий допустимый код Python, который при интерпретации является кодом, который вы хотите запустить / проверить. Эти оба работают:

#!/usr/bin/env python

def split_raw(val, sep='\n'):
  r"""Split a string on newlines (by default).

  >>> split_raw('alpha\nbeta\ngamma')
  ['alpha', 'beta', 'gamma']
  """
  return val.split(sep)


def split_esc(val, sep='\n'):
  """Split a string on newlines (by default).

  >>> split_esc('alpha\nbeta\ngamma')
  ['alpha', 'beta', 'gamma']
  """
  return val.split(sep)

import doctest
doctest.testmod()

эффект использования необработанных строк и эффект двойного экранирования (экранирования косой черты) оставляют в строке два символа: косую черту и n. Этот код передается в Интерпретатор Python, который принимает "slash then n", чтобы означать "символ новой строки" внутри строкового литерала.

используйте то, что вы предпочитаете.