diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst index 45a5e5062d55b6..721e8166b6ce1f 100644 --- a/Doc/whatsnew/3.12.rst +++ b/Doc/whatsnew/3.12.rst @@ -425,6 +425,11 @@ Deprecated :exc:`ImportWarning`). (Contributed by Brett Cannon in :gh:`65961`.) +* Deprecated using :term:`bytes-like objects ` except + :class:`bytes` and :class:`bytearray` in builtins :class:`int`, + :class:`float`, :func:`compile`, :func:`eval` and :func:`exec`. + (Contributed by Serhiy Storchaka in :gh:`71759`.) + * The :meth:`~asyncio.DefaultEventLoopPolicy.get_event_loop` method of the default event loop policy now emits a :exc:`DeprecationWarning` if there is no current event loop set and it decides to create one. diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9e19af0ae90fc1..8439d711393442 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -338,7 +338,8 @@ def test_compile(self): compile(source='pass', filename='?', mode='exec') compile(dont_inherit=False, filename='tmp', source='0', mode='eval') compile('pass', '?', dont_inherit=True, mode='exec') - compile(memoryview(b"text"), "name", "exec") + with self.assertWarns(DeprecationWarning): + compile(memoryview(b"text"), "name", "exec") self.assertRaises(TypeError, compile) self.assertRaises(ValueError, compile, 'print(42)\n', '', 'badmode') self.assertRaises(ValueError, compile, 'print(42)\n', '', 'single', 0xff) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 90b067bcf30912..cbe30aa1e21b4d 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -591,19 +591,25 @@ def test_null_terminated(self): # objects are accepted, which could be not terminated. with self.assertRaisesRegex(SyntaxError, "cannot contain null"): compile("123\x00", "", "eval") - with self.assertRaisesRegex(SyntaxError, "cannot contain null"): + with self.assertRaisesRegex(SyntaxError, "cannot contain null"), \ + self.assertWarns(DeprecationWarning): compile(memoryview(b"123\x00"), "", "eval") - code = compile(memoryview(b"123\x00")[1:-1], "", "eval") + with self.assertWarns(DeprecationWarning): + code = compile(memoryview(b"123\x00")[1:-1], "", "eval") self.assertEqual(eval(code), 23) - code = compile(memoryview(b"1234")[1:-1], "", "eval") + with self.assertWarns(DeprecationWarning): + code = compile(memoryview(b"1234")[1:-1], "", "eval") self.assertEqual(eval(code), 23) - code = compile(memoryview(b"$23$")[1:-1], "", "eval") + with self.assertWarns(DeprecationWarning): + code = compile(memoryview(b"$23$")[1:-1], "", "eval") self.assertEqual(eval(code), 23) # Also test when eval() and exec() do the compilation step - self.assertEqual(eval(memoryview(b"1234")[1:-1]), 23) + with self.assertWarns(DeprecationWarning): + self.assertEqual(eval(memoryview(b"1234")[1:-1]), 23) namespace = dict() - exec(memoryview(b"ax = 123")[1:-1], namespace) + with self.assertWarns(DeprecationWarning): + exec(memoryview(b"ax = 123")[1:-1], namespace) self.assertEqual(namespace['x'], 12) def check_constant(self, func, expected): diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index f8350c1e4caa27..4001e32a91b9dd 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -93,38 +93,42 @@ def test_underscores(self): def test_non_numeric_input_types(self): # Test possible non-numeric types for the argument x, including # subclasses of the explicitly documented accepted types. + def check(f): + x = f(b" 3.14 ") + self.assertEqual(float(x), 3.14) + with self.assertRaisesRegex(ValueError, "could not convert"): + float(f(b'A' * 0x10)) + class CustomStr(str): pass class CustomBytes(bytes): pass class CustomByteArray(bytearray): pass - factories = [ - bytes, - bytearray, - lambda b: CustomStr(b.decode()), - CustomBytes, - CustomByteArray, - memoryview, - ] + check(bytes) + check(bytearray) + check(lambda b: CustomStr(b.decode())) + check(CustomBytes) + check(CustomByteArray) + with self.assertWarns(DeprecationWarning): + check(memoryview) try: from array import array except ImportError: pass else: - factories.append(lambda b: array('B', b)) - - for f in factories: - x = f(b" 3.14 ") - with self.subTest(type(x)): - self.assertEqual(float(x), 3.14) - with self.assertRaisesRegex(ValueError, "could not convert"): - float(f(b'A' * 0x10)) + with self.assertWarns(DeprecationWarning): + check(lambda b: array('B', b)) def test_float_memoryview(self): - self.assertEqual(float(memoryview(b'12.3')[1:4]), 2.3) - self.assertEqual(float(memoryview(b'12.3\x00')[1:4]), 2.3) - self.assertEqual(float(memoryview(b'12.3 ')[1:4]), 2.3) - self.assertEqual(float(memoryview(b'12.3A')[1:4]), 2.3) - self.assertEqual(float(memoryview(b'12.34')[1:4]), 2.3) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(memoryview(b'12.3')[1:4]), 2.3) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(memoryview(b'12.3\x00')[1:4]), 2.3) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(memoryview(b'12.3 ')[1:4]), 2.3) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(memoryview(b'12.3A')[1:4]), 2.3) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(memoryview(b'12.34')[1:4]), 2.3) def test_error_message(self): def check(s): diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 334fea0774be51..7e10e1f8876a23 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -305,44 +305,48 @@ def __index__(self): def test_non_numeric_input_types(self): # Test possible non-numeric types for the argument x, including # subclasses of the explicitly documented accepted types. + def check(f): + x = f(b'100') + self.assertEqual(int(x), 100) + if isinstance(x, (str, bytes, bytearray)): + self.assertEqual(int(x, 2), 4) + else: + msg = "can't convert non-string" + with self.assertRaisesRegex(TypeError, msg): + int(x, 2) + with self.assertRaisesRegex(ValueError, 'invalid literal'): + int(f(b'A' * 0x10)) + class CustomStr(str): pass class CustomBytes(bytes): pass class CustomByteArray(bytearray): pass - factories = [ - bytes, - bytearray, - lambda b: CustomStr(b.decode()), - CustomBytes, - CustomByteArray, - memoryview, - ] + check(bytes) + check(bytearray) + check(lambda b: CustomStr(b.decode())) + check(CustomBytes) + check(CustomByteArray) + with self.assertWarns(DeprecationWarning): + check(memoryview) try: from array import array except ImportError: pass else: - factories.append(lambda b: array('B', b)) - - for f in factories: - x = f(b'100') - with self.subTest(type(x)): - self.assertEqual(int(x), 100) - if isinstance(x, (str, bytes, bytearray)): - self.assertEqual(int(x, 2), 4) - else: - msg = "can't convert non-string" - with self.assertRaisesRegex(TypeError, msg): - int(x, 2) - with self.assertRaisesRegex(ValueError, 'invalid literal'): - int(f(b'A' * 0x10)) + with self.assertWarns(DeprecationWarning): + check(lambda b: array('B', b)) def test_int_memoryview(self): - self.assertEqual(int(memoryview(b'123')[1:3]), 23) - self.assertEqual(int(memoryview(b'123\x00')[1:3]), 23) - self.assertEqual(int(memoryview(b'123 ')[1:3]), 23) - self.assertEqual(int(memoryview(b'123A')[1:3]), 23) - self.assertEqual(int(memoryview(b'1234')[1:3]), 23) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(memoryview(b'123')[1:3]), 23) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(memoryview(b'123\x00')[1:3]), 23) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(memoryview(b'123 ')[1:3]), 23) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(memoryview(b'123A')[1:3]), 23) + with self.assertWarns(DeprecationWarning): + self.assertEqual(int(memoryview(b'1234')[1:3]), 23) def test_string_float(self): self.assertRaises(ValueError, int, '1.2') diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 48f419e62fbbed..cbd7d39a688e25 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -761,12 +761,12 @@ def test_pdb_run_with_incorrect_argument(): >>> with pti: ... pdb_invoke('run', lambda x: x) Traceback (most recent call last): - TypeError: exec() arg 1 must be a string, bytes or code object + TypeError: exec() arg 1 must be a string, bytes, bytearray or code object >>> with pti: ... pdb_invoke('runeval', lambda x: x) Traceback (most recent call last): - TypeError: eval() arg 1 must be a string, bytes or code object + TypeError: eval() arg 1 must be a string, bytes, bytearray or code object """ diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-10-12-23-09-47.bpo-27572.B5ofbL.rst b/Misc/NEWS.d/next/Core and Builtins/2017-10-12-23-09-47.bpo-27572.B5ofbL.rst new file mode 100644 index 00000000000000..4aaa01f3bb2c05 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-10-12-23-09-47.bpo-27572.B5ofbL.rst @@ -0,0 +1,2 @@ +Deprecated using bytes-like objects except bytes and bytearray in builtins +int(), float(), compile(), eval() and exec(). diff --git a/Objects/abstract.c b/Objects/abstract.c index 9dc74fb9c2608c..04444d58a815bf 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -1599,6 +1599,12 @@ PyNumber_Long(PyObject *o) if (PyObject_GetBuffer(o, &view, PyBUF_SIMPLE) == 0) { PyObject *bytes; + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "int() argument must be a string, a bytes object, " + "a bytearray or a real number, not '%.100s'", + o->ob_type->tp_name)) { + return NULL; + } /* Copy to NUL-terminated buffer. */ bytes = PyBytes_FromStringAndSize((const char *)view.buf, view.len); if (bytes == NULL) { @@ -1612,8 +1618,8 @@ PyNumber_Long(PyObject *o) return result; } - return type_error("int() argument must be a string, a bytes-like object " - "or a real number, not '%.200s'", o); + return type_error("int() argument must be a string, a bytes object, " + "a bytearray or a real number, not '%.200s'", o); } PyObject * diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 912b742f797d24..f9b2ef8b58c780 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -223,6 +223,12 @@ PyFloat_FromString(PyObject *v) len = PyByteArray_GET_SIZE(v); } else if (PyObject_GetBuffer(v, &view, PyBUF_SIMPLE) == 0) { + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "float() argument must be a string, a bytes object, " + "a bytearray or a real number, not '%.100s'", + v->ob_type->tp_name)) { + return NULL; + } s = (const char *)view.buf; len = view.len; /* Copy to NUL-terminated buffer. */ @@ -235,7 +241,8 @@ PyFloat_FromString(PyObject *v) } else { PyErr_Format(PyExc_TypeError, - "float() argument must be a string or a real number, not '%.200s'", + "float() argument must be a string, a bytes object, " + "a bytearray or a real number, not '%.200s'", Py_TYPE(v)->tp_name); return NULL; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 53439ab16040c4..4347865ebba4f0 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -821,7 +821,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename, goto finally; } - str = _Py_SourceAsString(source, "compile", "string, bytes or AST", &cf, &source_copy); + str = _Py_SourceAsString(source, "compile", "string, bytes, bytearray or AST", &cf, &source_copy); if (str == NULL) goto error; @@ -958,7 +958,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, PyCompilerFlags cf = _PyCompilerFlags_INIT; cf.cf_flags = PyCF_SOURCE_IS_UTF8; - str = _Py_SourceAsString(source, "eval", "string, bytes or code", &cf, &source_copy); + str = _Py_SourceAsString(source, "eval", "string, bytes, bytearray or code", &cf, &source_copy); if (str == NULL) return NULL; @@ -1093,7 +1093,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, PyCompilerFlags cf = _PyCompilerFlags_INIT; cf.cf_flags = PyCF_SOURCE_IS_UTF8; str = _Py_SourceAsString(source, "exec", - "string, bytes or code", &cf, + "string, bytes, bytearray or code", &cf, &source_copy); if (str == NULL) return NULL; diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 6a4d593768690a..127520b07c87cf 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1826,6 +1826,12 @@ _Py_SourceAsString(PyObject *cmd, const char *funcname, const char *what, PyComp size = PyByteArray_GET_SIZE(cmd); } else if (PyObject_GetBuffer(cmd, &view, PyBUF_SIMPLE) == 0) { + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "%s() arg 1 must be a %s object, not '%.100s'", + funcname, what, + cmd->ob_type->tp_name)) { + return NULL; + } /* Copy to NUL-terminated buffer. */ *cmd_copy = PyBytes_FromStringAndSize( (const char *)view.buf, view.len);