]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127350: Add more tests for Py_fopen() (GH-128587)
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 7 Jan 2025 21:13:08 +0000 (23:13 +0200)
committerGitHub <noreply@github.com>
Tue, 7 Jan 2025 21:13:08 +0000 (21:13 +0000)
Lib/test/test_capi/test_file.py
Modules/_testcapi/clinic/file.c.h
Modules/_testcapi/file.c

index 8a08a0a93eb8b77dc74c26477f59496ac22d06ab..a67a5121c4588bf46be1bee2f42d131a43562246 100644 (file)
@@ -5,6 +5,8 @@ from test.support import import_helper, os_helper
 
 _testcapi = import_helper.import_module('_testcapi')
 
+NULL = None
+
 
 class CAPIFileTest(unittest.TestCase):
     def test_py_fopen(self):
@@ -25,7 +27,9 @@ class CAPIFileTest(unittest.TestCase):
             os_helper.TESTFN,
             os.fsencode(os_helper.TESTFN),
         ]
-        # TESTFN_UNDECODABLE cannot be used to create a file on macOS/WASI.
+        if os_helper.TESTFN_UNDECODABLE is not None:
+            filenames.append(os_helper.TESTFN_UNDECODABLE)
+            filenames.append(os.fsdecode(os_helper.TESTFN_UNDECODABLE))
         if os_helper.TESTFN_UNENCODABLE is not None:
             filenames.append(os_helper.TESTFN_UNENCODABLE)
         for filename in filenames:
@@ -33,7 +37,12 @@ class CAPIFileTest(unittest.TestCase):
                 try:
                     with open(filename, "wb") as fp:
                         fp.write(source)
-
+                except OSError:
+                    # TESTFN_UNDECODABLE cannot be used to create a file
+                    # on macOS/WASI.
+                    filename = None
+                    continue
+                try:
                     data = _testcapi.py_fopen(filename, "rb")
                     self.assertEqual(data, source[:256])
                 finally:
@@ -47,7 +56,14 @@ class CAPIFileTest(unittest.TestCase):
 
         # non-ASCII mode failing with "Invalid argument"
         with self.assertRaises(OSError):
-            _testcapi.py_fopen(__file__, "\xe9")
+            _testcapi.py_fopen(__file__, b"\xc2\x80")
+        with self.assertRaises(OSError):
+            # \x98 is invalid in cp1250, cp1251, cp1257
+            # \x9d is invalid in cp1252-cp1255, cp1258
+            _testcapi.py_fopen(__file__, b"\xc2\x98\xc2\x9d")
+        # UnicodeDecodeError can come from the audit hook code
+        with self.assertRaises((UnicodeDecodeError, OSError)):
+            _testcapi.py_fopen(__file__, b"\x98\x9d")
 
         # invalid filename type
         for invalid_type in (123, object()):
@@ -60,7 +76,8 @@ class CAPIFileTest(unittest.TestCase):
                 # On Windows, the file mode is limited to 10 characters
                 _testcapi.py_fopen(__file__, "rt+, ccs=UTF-8")
 
-        # CRASHES py_fopen(__file__, None)
+        # CRASHES _testcapi.py_fopen(NULL, 'rb')
+        # CRASHES _testcapi.py_fopen(__file__, NULL)
 
 
 if __name__ == "__main__":
index 2ca21fffcef680d661a621144c3ac5882a1d33ee..fddbf48071bd3b8e097fa0c4a274b737bcc555f7 100644 (file)
@@ -14,7 +14,8 @@ PyDoc_STRVAR(_testcapi_py_fopen__doc__,
     {"py_fopen", _PyCFunction_CAST(_testcapi_py_fopen), METH_FASTCALL, _testcapi_py_fopen__doc__},
 
 static PyObject *
-_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode);
+_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode,
+                        Py_ssize_t mode_length);
 
 static PyObject *
 _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
@@ -22,27 +23,15 @@ _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
     PyObject *return_value = NULL;
     PyObject *path;
     const char *mode;
-
-    if (!_PyArg_CheckPositional("py_fopen", nargs, 2, 2)) {
-        goto exit;
-    }
-    path = args[0];
-    if (!PyUnicode_Check(args[1])) {
-        _PyArg_BadArgument("py_fopen", "argument 2", "str", args[1]);
-        goto exit;
-    }
     Py_ssize_t mode_length;
-    mode = PyUnicode_AsUTF8AndSize(args[1], &mode_length);
-    if (mode == NULL) {
-        goto exit;
-    }
-    if (strlen(mode) != (size_t)mode_length) {
-        PyErr_SetString(PyExc_ValueError, "embedded null character");
+
+    if (!_PyArg_ParseStack(args, nargs, "Oz#:py_fopen",
+        &path, &mode, &mode_length)) {
         goto exit;
     }
-    return_value = _testcapi_py_fopen_impl(module, path, mode);
+    return_value = _testcapi_py_fopen_impl(module, path, mode, mode_length);
 
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=c9fe964c3e5a0c32 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c4dc92400306c3eb input=a9049054013a1b77]*/
index 4bad43010fd440bc8265e8a9e2c4f001d58a34a4..d15173fc7959e5eb15cf474f167ba435f2efa388 100644 (file)
@@ -14,16 +14,18 @@ module _testcapi
 _testcapi.py_fopen
 
     path: object
-    mode: str
+    mode: str(zeroes=True, accept={robuffer, str, NoneType})
     /
 
 Call Py_fopen(), fread(256) and Py_fclose(). Return read bytes.
 [clinic start generated code]*/
 
 static PyObject *
-_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode)
-/*[clinic end generated code: output=5a900af000f759de input=d7e7b8f0fd151953]*/
+_testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode,
+                        Py_ssize_t mode_length)
+/*[clinic end generated code: output=69840d0cfd8b7fbb input=f3a579dd7eb60926]*/
 {
+    NULLABLE(path);
     FILE *fp = Py_fopen(path, mode);
     if (fp == NULL) {
         return NULL;