【问题标题】:pytest: assert escaped characters with re.escape() failspytest:使用 re.escape() 断言转义字符失败
【发布时间】:2019-11-11 06:28:25
【问题描述】:

我有一个使用re.escape() 方法返回字符的函数。 在经验测试中它似乎有效,我想用 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('!')的输出我得到"!"而不是"\!",这在解释器上不会发生。

我试图通过强制"\!" 作为输出来进行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

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

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

【问题讨论】:

  • 我在 pytest 中没有收到assert re.escape("!") == "\\!" 的断言错误。 (Python 3.6.7,pytest 5.2.2)
  • 是的!是真的!使用 python3.6 一切正常,我使用的是 3.7

标签: python regex escaping pytest


【解决方案1】:

如果您查看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('!')返回!,而不是\!

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

例如会起作用。

更新

此答案适用于 Python 3.7,它适用于 Python 3.6。 Pull request #1007改了escape()pull source code

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)

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

【讨论】:

  • 那么您能解释一下为什么assert re.escape('!') == "\\!" 在控制台中工作吗?
  • 奇怪。它对我有用。 (Linux 中的 Python 3.6.7)
  • 我编辑了消息,问题取决于python或pytest的版本
  • 好的,你的答案是正确的(在更正之前也是正确的),不是错误,而是功能的变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
相关资源
最近更新 更多