pytest: утвердить экранированные символы с помощью re.escape () не удается - PullRequest
2 голосов
/ 11 ноября 2019

У меня есть функция, которая возвращает символы, используя метод re.escape(). В эмпирических тестах это, кажется, работает, я хотел проверить это с pytest. Но я не смог заставить тесты работать, поэтому после нескольких попыток я попробовал что-то подобное:

    def test_escape():
>       assert re.escape('!') == "\\!"
E       AssertionError: assert '!' == '\\!'
E         - !
E         + \!

test/test_e.py:6: AssertionError

Я также проверил это с переводчиком, который работает без проблем:

>>> re.escape('!') == '\\!'
True

отключение захвата вывода pytest с помощью "-s" и попытка напечатать вывод re.escape('!') Я получаю "!", а не "\!", что не происходит в интерпретаторе.

Я попытался сделать monkeypatch re.escape, указав "\!" в качестве вывода, и он волшебным образом работает. Это, очевидно, не решает мою проблему, но выдвигает на первый план некоторую неизвестную мне проблему с re.escape

@pytest.fixture
def mock_escape(monkeypatch):
    monkeypatch.setattr(re, "escape", lambda x: "\\!")

def test_escape(mock_escape):
    assert re.escape('!') == "\\!"

...

test/test_e.py .

======================================== 1 passed in 0.07s =========================================
all test passed

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

# EDIT: # , как обнаружил tmt, это проблема с версией python или pytest. Проблема возникает с python 3.7.2 и pytest 5.2.1. Проблема НЕ возникает с python 3.6.3 и pytest 4.5.0 Так что это почти наверняка ошибка (на мой взгляд, легче всего pytest) Как ответит парень, это просто изменение поведения re. escape ()

1 Ответ

2 голосов
/ 11 ноября 2019

Если вы посмотрите на re.py , вы увидите, что escape() использует определенный список специальных символов

_special_chars_map = {i: '\\' + chr(i) for i in b'()[]{}?*+-|^$\\.&~# \t\n\r\v\f'}

def escape(pattern):
    """
    Escape special characters in a string.
    """
    if isinstance(pattern, str):
        return pattern.translate(_special_chars_map)
    else:
        pattern = str(pattern, 'latin1')
        return pattern.translate(_special_chars_map).encode('latin1')

и ! там не включены, поэтому re.escape('!')например, return !, а не \!.

assert re.escape('[') == '\\['

.

Обновление :

Этот ответ для Python 3.7, это работает на Python 3.6. Запрос на извлечение # 1007 изменен escape() Исходный код извлечения

re.escape () теперь экранирует только специальные символы.

Предыдущая версия:

_alphanum_str = frozenset("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")
_alphanum_bytes = frozenset(b"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890")

def escape(pattern):
    if isinstance(pattern, str):
        alphanum = _alphanum_str
        s = list(pattern)
        for i, c in enumerate(pattern):
            if c not in alphanum:
                if c == "\000":
                    s[i] = "\\000"
                else:
                    s[i] = "\\" + c
        return "".join(s)
    else:
        alphanum = _alphanum_bytes
        s = []
        esc = ord(b"\\")
        for c in pattern:
            if c in alphanum:
                s.append(c)
            else:
                if c == 0:
                    s.extend(b"\\000")
                else:
                    s.append(esc)
                    s.append(c)
        return bytes(s)

Она была изменена 13 апреля 2017 года, поэтому просмотр истории версий re.escape('!') == '\\!' должен работать на Python 3.6 и более старых версиях.

...