diff --git a/netlib/utils.py b/netlib/utils.py index fe11cb5be..73b2adb31 100644 --- a/netlib/utils.py +++ b/netlib/utils.py @@ -425,6 +425,10 @@ def safe_subn(pattern, repl, target, *args, **kwargs): def bytes_to_escaped_str(data): """ Take bytes and return a safe string that can be displayed to the user. + + Single quotes are always escaped, double quotes are never escaped: + "'" + bytes_to_escaped_str(...) + "'" + gives a valid Python string. """ # TODO: We may want to support multi-byte characters without escaping them. # One way to do would be calling .decode("utf8", "backslashreplace") first @@ -432,7 +436,9 @@ def bytes_to_escaped_str(data): if not isinstance(data, bytes): raise ValueError("data must be bytes") - return repr(data).lstrip("b")[1:-1] + # We always insert a double-quote here so that we get a single-quoted string back + # https://stackoverflow.com/questions/29019340/why-does-python-use-different-quotes-for-representing-strings-depending-on-their + return repr('"' + data).lstrip("b")[2:-1] def escaped_str_to_bytes(data): diff --git a/test/netlib/test_utils.py b/test/netlib/test_utils.py index 671ae66c4..fce1d0a70 100644 --- a/test/netlib/test_utils.py +++ b/test/netlib/test_utils.py @@ -178,6 +178,8 @@ def test_bytes_to_escaped_str(): assert utils.bytes_to_escaped_str(b"\b") == r"\x08" assert utils.bytes_to_escaped_str(br"&!?=\)") == r"&!?=\\)" assert utils.bytes_to_escaped_str(b'\xc3\xbc') == r"\xc3\xbc" + assert utils.bytes_to_escaped_str(b"'") == r"\'" + assert utils.bytes_to_escaped_str(b'"') == r'"' def test_escaped_str_to_bytes(): diff --git a/test/pathod/test_language_base.py b/test/pathod/test_language_base.py index 64d4af1fe..2e5d9041e 100644 --- a/test/pathod/test_language_base.py +++ b/test/pathod/test_language_base.py @@ -67,7 +67,7 @@ class TestTokValueLiteral: def test_roundtrip(self): self.roundtrip("'") - self.roundtrip('\'') + self.roundtrip(r"\'") self.roundtrip("a") self.roundtrip("\"") # self.roundtrip("\\")