gh-127350: Add more tests for Py_fopen() (GH-128587)

This commit is contained in:
Serhiy Storchaka 2025-01-07 23:13:08 +02:00 committed by GitHub
parent a21e31ec54
commit 07e6aa2efc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 25 deletions

View file

@ -5,6 +5,8 @@
_testcapi = import_helper.import_module('_testcapi')
NULL = None
class CAPIFileTest(unittest.TestCase):
def test_py_fopen(self):
@ -25,7 +27,9 @@ def test_py_fopen(self):
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 @@ def test_py_fopen(self):
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 @@ def test_py_fopen(self):
# 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 @@ def test_py_fopen(self):
# 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__":

View 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) {
if (!_PyArg_ParseStack(args, nargs, "Oz#:py_fopen",
&path, &mode, &mode_length)) {
goto exit;
}
if (strlen(mode) != (size_t)mode_length) {
PyErr_SetString(PyExc_ValueError, "embedded null character");
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]*/

View 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;