Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-37367: octal escapes applied inconsistently throughout the interpreter and lib #14654

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import tempfile
import textwrap
import unittest
import ast
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the imports sorted alphabetically.


import test.support
import test.string_tests
Expand Down Expand Up @@ -962,6 +963,45 @@ def test_translate(self):
c = b.translate(None, delete=b'e')
self.assertEqual(c, b'hllo')

def test_octal_values(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not correct place for such test. This file is for tests of bytes and bytearray methods, not parsing Python code.

test_string_literals is for parsing string and bytes literals. Add some tests there.

# bpo-37367: verify that octal escape sequences
# with values greater than 255 raise ValueError

# test 1- and 2- digit octal escape sequences
# (i.e. no leading zeroes)
for i in range(0o00, 0o77):
v = "b'\\{0:o}'".format(i)
self.type2test(ast.literal_eval(v))
self.assertEqual(ord(ast.literal_eval(v)), i)

# test 3-digit octal escape sequences
# (including leading zeroes)
for i in range(0o00, 0o400):
v = "b'\\{0:03o}'".format(i)
self.type2test(ast.literal_eval(v))
self.assertEqual(ord(ast.literal_eval(v)), i)

raised = 0
for i in range(0o400, 0o1000):
try:
self.type2test(ast.literal_eval("b'\\{0:o}'".format(i)))
except SyntaxError as e:
# ast.literal_eval() raises SyntaxError and
# mentions the underlying ValueError in
# the returned string.
self.assertEqual(str(e).find("(value error) octal value "
"must be in range(0, 256)", 0),
0)
raised = raised + 1
self.assertEqual(raised, 0o1000-0o400)

# test 4-digit octal value (4th digit should be treated as literal)
self.assertEqual(ast.literal_eval("b'\\1234'"), b'S4')
self.assertRaises(SyntaxError, ast.literal_eval, "b'\\4321'")

# specific case mentioned in bpo-37367
self.assertRaises(SyntaxError, eval, "ord(b'\\407')")


class BytesTest(BaseBytesTest, unittest.TestCase):
type2test = bytes
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ def test_escape(self):
check(br"[\418]", b"[!8]")
check(br"[\101]", b"[A]")
check(br"[\1010]", b"[A0]")
check(br"[\501]", b"[A]")
self.assertRaises(ValueError, decode, br"[\501]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move it to test_errors below.

check(br"[\x41]", b"[A]")
check(br"[\x410]", b"[A0]")
for i in range(97, 123):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Byte strings containing octal escape sequences with values greater than 255
will now raise ValueError. Patch by Jeffrey Kintscher.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to mention the previous behavior here.

5 changes: 5 additions & 0 deletions Objects/bytesobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,11 @@ PyObject *_PyBytes_DecodeEscape(const char *s,
if (s < end && '0' <= *s && *s <= '7')
c = (c<<3) + *s++ - '0';
}
if (c > 255) {
PyErr_Format(PyExc_ValueError,
"octal value must be in range(0, 256)");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message should contain the position of the invalid octal escape sequence. It would be nice if look similar to error message for invalid hexadecimal escape sequence. For example:

"invalid octal escape sequence at position %zd"

goto failed;
}
*p++ = c;
break;
case 'x':
Expand Down