summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2022-10-18 15:52:31 (GMT)
committerGitHub <noreply@github.com>2022-10-18 15:52:31 (GMT)
commitdb03c8066a6b12fa23618f9add0eb795936726b4 (patch)
tree71d35cac37736188bbe8b046aab657b0892be1de
parent9da5215000920870eeddd78af92da4c98099706c (diff)
downloadcpython-db03c8066a6b12fa23618f9add0eb795936726b4.zip
cpython-db03c8066a6b12fa23618f9add0eb795936726b4.tar.gz
cpython-db03c8066a6b12fa23618f9add0eb795936726b4.tar.bz2
gh-98393: os module reject bytes-like, only accept bytes (#98394)
The os module and the PyUnicode_FSDecoder() function no longer accept bytes-like paths, like bytearray and memoryview types: only the exact bytes type is accepted for bytes strings.
-rw-r--r--Doc/whatsnew/3.12.rst10
-rw-r--r--Lib/test/test_compile.py5
-rw-r--r--Lib/test/test_os.py32
-rw-r--r--Lib/test/test_posix.py9
-rw-r--r--Lib/test/test_symtable.py5
-rw-r--r--Misc/NEWS.d/next/C API/2022-10-18-16-16-27.gh-issue-98393.55u4BF.rst3
-rw-r--r--Misc/NEWS.d/next/Library/2022-10-18-15-41-37.gh-issue-98393.vhPu4L.rst3
-rw-r--r--Modules/posixmodule.c32
-rw-r--r--Objects/unicodeobject.c38
9 files changed, 48 insertions, 89 deletions
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index ad0270a..525efc4 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -515,6 +515,11 @@ Changes in the Python API
in Python 3.9.
(Contributed by Victor Stinner in :gh:`94352`.)
+* The :mod:`os` module no longer accepts bytes-like paths, like
+ :class:`bytearray` and :class:`memoryview` types: only the exact
+ :class:`bytes` type is accepted for bytes strings.
+ (Contributed by Victor Stinner in :gh:`98393`.)
+
Build Changes
=============
@@ -631,6 +636,11 @@ Porting to Python 3.12
to traverse and clear their instance's dictionaries.
To clear weakrefs, call :c:func:`PyObject_ClearWeakRefs`, as before.
+* The :c:func:`PyUnicode_FSDecoder` function no longer accepts bytes-like
+ paths, like :class:`bytearray` and :class:`memoryview` types: only the exact
+ :class:`bytes` type is accepted for bytes strings.
+ (Contributed by Victor Stinner in :gh:`98393`.)
+
Deprecated
----------
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 8bf8470..f5c0c76 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -494,9 +494,8 @@ if 1:
code = compile('pass', filename, 'exec')
self.assertEqual(code.co_filename, 'file.py')
for filename in bytearray(b'file.py'), memoryview(b'file.py'):
- with self.assertWarns(DeprecationWarning):
- code = compile('pass', filename, 'exec')
- self.assertEqual(code.co_filename, 'file.py')
+ with self.assertRaises(TypeError):
+ compile('pass', filename, 'exec')
self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec')
@support.cpython_only
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 2dafb78..10dbb2b 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -3860,18 +3860,18 @@ class OSErrorTests(unittest.TestCase):
for filenames, func, *func_args in funcs:
for name in filenames:
- try:
- if isinstance(name, (str, bytes)):
+ if not isinstance(name, (str, bytes)):
+ with self.assertRaises(TypeError):
func(name, *func_args)
- else:
- with self.assertWarnsRegex(DeprecationWarning, 'should be'):
- func(name, *func_args)
- except OSError as err:
- self.assertIs(err.filename, name, str(func))
- except UnicodeDecodeError:
- pass
else:
- self.fail("No exception thrown by {}".format(func))
+ try:
+ func(name, *func_args)
+ except OSError as err:
+ self.assertIs(err.filename, name, str(func))
+ except UnicodeDecodeError:
+ pass
+ else:
+ self.fail("No exception thrown by {}".format(func))
class CPUCountTests(unittest.TestCase):
def test_cpu_count(self):
@@ -4350,16 +4350,8 @@ class TestScandir(unittest.TestCase):
for cls in bytearray, memoryview:
path_bytes = cls(os.fsencode(self.path))
- with self.assertWarns(DeprecationWarning):
- entries = list(os.scandir(path_bytes))
- self.assertEqual(len(entries), 1, entries)
- entry = entries[0]
-
- self.assertEqual(entry.name, b'file.txt')
- self.assertEqual(entry.path,
- os.fsencode(os.path.join(self.path, 'file.txt')))
- self.assertIs(type(entry.name), bytes)
- self.assertIs(type(entry.path), bytes)
+ with self.assertRaises(TypeError):
+ list(os.scandir(path_bytes))
@unittest.skipUnless(os.listdir in os.supports_fd,
'fd support for listdir required for this test.')
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index e643d8e..be561af 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -634,7 +634,7 @@ class PosixTester(unittest.TestCase):
self.assertTrue(posix.stat(os_helper.TESTFN))
self.assertTrue(posix.stat(os.fsencode(os_helper.TESTFN)))
- self.assertWarnsRegex(DeprecationWarning,
+ self.assertRaisesRegex(TypeError,
'should be string, bytes, os.PathLike or integer, not',
posix.stat, bytearray(os.fsencode(os_helper.TESTFN)))
self.assertRaisesRegex(TypeError,
@@ -841,11 +841,8 @@ class PosixTester(unittest.TestCase):
def test_listdir_bytes_like(self):
for cls in bytearray, memoryview:
- with self.assertWarns(DeprecationWarning):
- names = posix.listdir(cls(b'.'))
- self.assertIn(os.fsencode(os_helper.TESTFN), names)
- for name in names:
- self.assertIs(type(name), bytes)
+ with self.assertRaises(TypeError):
+ posix.listdir(cls(b'.'))
@unittest.skipUnless(posix.listdir in os.supports_fd,
"test needs fd support for posix.listdir()")
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index 819354e..25714ae 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -222,10 +222,9 @@ class SymtableTest(unittest.TestCase):
checkfilename("def f(x): foo)(", 14) # parse-time
checkfilename("def f(x): global x", 11) # symtable-build-time
symtable.symtable("pass", b"spam", "exec")
- with self.assertWarns(DeprecationWarning), \
- self.assertRaises(TypeError):
+ with self.assertRaises(TypeError):
symtable.symtable("pass", bytearray(b"spam"), "exec")
- with self.assertWarns(DeprecationWarning):
+ with self.assertRaises(TypeError):
symtable.symtable("pass", memoryview(b"spam"), "exec")
with self.assertRaises(TypeError):
symtable.symtable("pass", list(b"spam"), "exec")
diff --git a/Misc/NEWS.d/next/C API/2022-10-18-16-16-27.gh-issue-98393.55u4BF.rst b/Misc/NEWS.d/next/C API/2022-10-18-16-16-27.gh-issue-98393.55u4BF.rst
new file mode 100644
index 0000000..658f587
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-10-18-16-16-27.gh-issue-98393.55u4BF.rst
@@ -0,0 +1,3 @@
+The :c:func:`PyUnicode_FSDecoder` function no longer accepts bytes-like
+paths, like :class:`bytearray` and :class:`memoryview` types: only the exact
+:class:`bytes` type is accepted for bytes strings. Patch by Victor Stinner.
diff --git a/Misc/NEWS.d/next/Library/2022-10-18-15-41-37.gh-issue-98393.vhPu4L.rst b/Misc/NEWS.d/next/Library/2022-10-18-15-41-37.gh-issue-98393.vhPu4L.rst
new file mode 100644
index 0000000..54e45bb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-10-18-15-41-37.gh-issue-98393.vhPu4L.rst
@@ -0,0 +1,3 @@
+The :mod:`os` module no longer accepts bytes-like paths, like
+:class:`bytearray` and :class:`memoryview` types: only the exact
+:class:`bytes` type is accepted for bytes strings. Patch by Victor Stinner.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 1e556fc..39198cb 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -1120,7 +1120,7 @@ path_converter(PyObject *o, void *p)
path_t *path = (path_t *)p;
PyObject *bytes = NULL;
Py_ssize_t length = 0;
- int is_index, is_buffer, is_bytes, is_unicode;
+ int is_index, is_bytes, is_unicode;
const char *narrow;
#ifdef MS_WINDOWS
PyObject *wo = NULL;
@@ -1158,11 +1158,10 @@ path_converter(PyObject *o, void *p)
/* Only call this here so that we don't treat the return value of
os.fspath() as an fd or buffer. */
is_index = path->allow_fd && PyIndex_Check(o);
- is_buffer = PyObject_CheckBuffer(o);
is_bytes = PyBytes_Check(o);
is_unicode = PyUnicode_Check(o);
- if (!is_index && !is_buffer && !is_unicode && !is_bytes) {
+ if (!is_index && !is_unicode && !is_bytes) {
/* Inline PyOS_FSPath() for better error messages. */
PyObject *func, *res;
@@ -1225,27 +1224,6 @@ path_converter(PyObject *o, void *p)
bytes = o;
Py_INCREF(bytes);
}
- else if (is_buffer) {
- /* XXX Replace PyObject_CheckBuffer with PyBytes_Check in other code
- after removing support of non-bytes buffer objects. */
- if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
- "%s%s%s should be %s, not %.200s",
- path->function_name ? path->function_name : "",
- path->function_name ? ": " : "",
- path->argument_name ? path->argument_name : "path",
- path->allow_fd && path->nullable ? "string, bytes, os.PathLike, "
- "integer or None" :
- path->allow_fd ? "string, bytes, os.PathLike or integer" :
- path->nullable ? "string, bytes, os.PathLike or None" :
- "string, bytes or os.PathLike",
- _PyType_Name(Py_TYPE(o)))) {
- goto error_exit;
- }
- bytes = PyBytes_FromObject(o);
- if (!bytes) {
- goto error_exit;
- }
- }
else if (is_index) {
if (!_fd_converter(o, &path->fd)) {
goto error_exit;
@@ -4126,8 +4104,8 @@ _posix_listdir(path_t *path, PyObject *list)
const char *name;
if (path->narrow) {
name = path->narrow;
- /* only return bytes if they specified a bytes-like object */
- return_str = !PyObject_CheckBuffer(path->object);
+ /* only return bytes if they specified a bytes object */
+ return_str = !PyBytes_Check(path->object);
}
else {
name = ".";
@@ -14049,7 +14027,7 @@ DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name,
goto error;
}
- if (!path->narrow || !PyObject_CheckBuffer(path->object)) {
+ if (!path->narrow || !PyBytes_Check(path->object)) {
entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len);
if (joined_path)
entry->path = PyUnicode_DecodeFSDefault(joined_path);
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 5b737f1..d090915 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -3619,48 +3619,25 @@ PyUnicode_FSConverter(PyObject* arg, void* addr)
int
PyUnicode_FSDecoder(PyObject* arg, void* addr)
{
- int is_buffer = 0;
- PyObject *path = NULL;
- PyObject *output = NULL;
if (arg == NULL) {
Py_DECREF(*(PyObject**)addr);
*(PyObject**)addr = NULL;
return 1;
}
- is_buffer = PyObject_CheckBuffer(arg);
- if (!is_buffer) {
- path = PyOS_FSPath(arg);
- if (path == NULL) {
- return 0;
- }
- }
- else {
- path = arg;
- Py_INCREF(arg);
+ PyObject *path = PyOS_FSPath(arg);
+ if (path == NULL) {
+ return 0;
}
+ PyObject *output = NULL;
if (PyUnicode_Check(path)) {
output = path;
}
- else if (PyBytes_Check(path) || is_buffer) {
- PyObject *path_bytes = NULL;
-
- if (!PyBytes_Check(path) &&
- PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
- "path should be string, bytes, or os.PathLike, not %.200s",
- Py_TYPE(arg)->tp_name)) {
- Py_DECREF(path);
- return 0;
- }
- path_bytes = PyBytes_FromObject(path);
+ else if (PyBytes_Check(path)) {
+ output = PyUnicode_DecodeFSDefaultAndSize(PyBytes_AS_STRING(path),
+ PyBytes_GET_SIZE(path));
Py_DECREF(path);
- if (!path_bytes) {
- return 0;
- }
- output = PyUnicode_DecodeFSDefaultAndSize(PyBytes_AS_STRING(path_bytes),
- PyBytes_GET_SIZE(path_bytes));
- Py_DECREF(path_bytes);
if (!output) {
return 0;
}
@@ -3672,6 +3649,7 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr)
Py_DECREF(path);
return 0;
}
+
if (findchar(PyUnicode_DATA(output), PyUnicode_KIND(output),
PyUnicode_GET_LENGTH(output), 0, 1) >= 0) {
PyErr_SetString(PyExc_ValueError, "embedded null character");