Python `doctest` с экранами ANSI

У меня есть код, для которого у меня есть doctest, например.

def foo(x):
    """
    Examples:
        >>> s = foo()
        >>> print(repr(s))
        '\x1b[0;35mfoo\x1b[0m \x1b[0;36mbar\x1b[0m'
        >>> print(s)
        foo bar
    """
    return '\u001b[0;35mfoo\u001b[0m \u001b[0;36mbar\u001b[0m'

Конечно, в терминале, поддерживающем экранирование ANSI, последние print() на самом деле окрашены.

Однако, если я запущу doctest здесь, я получу провальный тест. Как я мог это решить?

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


person norok2    schedule 09.07.2019    source источник


Ответы (1)


Глупый обходной путь для этого состоит в том, чтобы использовать сравнение в коде вместо того, чтобы полагаться на сравнения doctests, например:

def foo(x):
    """
    Examples:
        >>> s = foo()
        >>> print(repr(s))
        '\x1b[0;35mfoo\x1b[0m \x1b[0;36mbar\x1b[0m'
        >>> print(s == '\x1b[0;35mfoo\x1b[0m \x1b[0;36mbar\x1b[0m')
        True
    """
    return '\u001b[0;35mfoo\u001b[0m \u001b[0;36mbar\u001b[0m'

Другой возможностью может быть фильтр out ANSI экранирует либо прямо внутри print():

import re


def remove_ansi_escapes(text):
    ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
    return ansi_escape.sub('', text)


def foo(x):
    """
    Examples:
        >>> s = foo()
        >>> print(repr(s))
        '\x1b[0;35mfoo\x1b[0m \x1b[0;36mbar\x1b[0m'
        >>> print(remove_ansi_escapes(s))
        foo bar
    """
    return '\u001b[0;35mfoo\u001b[0m \u001b[0;36mbar\u001b[0m'

или с помощью захвата STDOUT, если печать происходит глубже в коде:

from contextlib import redirect_stdout
import io


def foo(x):
    """
    Examples:
        >>> buffer = io.StringIO()
        >>> with redirect_stdout(buffer):
        ...     foo()
        >>> print(remove_ansi_escapes(buffer.getvalue()))
        foo bar
    """
    s = '\u001b[0;35mfoo\u001b[0m \u001b[0;36mbar\u001b[0m'
    print(s)
person norok2    schedule 07.08.2019