pytest:使用 re.escape() 断言转义字符失败

pytest: assert escaped characters with re.escape() fails

我有一个函数可以使用 re.escape() 方法 returns 个字符。 在实证测试中它似乎有效,我想用 pytest 测试它。 但是我无法让测试正常工作,所以在尝试了几次之后,我尝试了类似的方法:

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

test/test_e.py:6: AssertionError

我也用解释器测试过,没有任何问题:

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

禁用使用“-s”捕获pytest的输出并尝试打印re.escape('!')的输出我得到"!"而不是"\!",这不会发生口译员。

我试图通过强制 "\!" 作为输出来对 re.escape 进行 monkeypatch,它神奇地起作用了。这显然不能解决我的问题,但突出显示了我不知道的某种问题 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

出于好奇,我对我的原始函数做了同样的事情(没有 monkeypatching 但编辑它的 return),即使在这种情况下它也能工作。 所以这不是因为导入而发生的问题。

# 编辑:# tmt发现是python或者pytest版本的问题。 python 3.7.2 和 pytest 5.2.1 会出现问题。 python 3.6.3 和 pytest 4.5.0 不会出现此问题 所以这几乎可以肯定是一个错误(在我看来更容易出现 pytest) 正如家伙的回复,这只是 re.escape()

的行为改变

如果您查看 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。 Pull request #1007 changed escape() pull source code

re.escape() now escapes only special characters.

以前的版本:

_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 == "[=12=]0":
                    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)

它于 2017 年 4 月 13 日修改,所以查看 versions history re.escape('!') == '\!' 应该适用于 Python 3.6 和更早的版本。