diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py index 1a55c761054e44..b39f499a2472b3 100644 --- a/Lib/test/test_capi/test_file.py +++ b/Lib/test/test_capi/test_file.py @@ -294,7 +294,31 @@ def test_py_fopen(self): # CRASHES py_fopen(NULL, 'rb') # CRASHES py_fopen(__file__, NULL) - # TODO: Test Py_UniversalNewlineFgets() + def test_py_universalnewlinefgets(self): + py_universalnewlinefgets = _testcapi.py_universalnewlinefgets + filename = os_helper.TESTFN + self.addCleanup(os_helper.unlink, filename) + + with open(filename, "wb") as fp: + fp.write(b"line1\nline2") + + with open(filename, "rb") as fp: + line = py_universalnewlinefgets(fp, 1000) + self.assertEqual(line, b"line1\n") + + with open(filename, "wb") as fp: + fp.write(b"line2\r\nline3") + + with open(filename, "rb") as fp: + line = py_universalnewlinefgets(fp, 1000) + self.assertEqual(line, b"line2\n") + + with open(filename, "wb") as fp: + fp.write(b"line3\rline4") + + with open(filename, "rb") as fp: + line = py_universalnewlinefgets(fp, 1000) + self.assertEqual(line, b"line3\n") # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by # test_embed.test_open_code_hook() diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h index 6efb6b47353443..08df729cb97738 100644 --- a/Modules/_testcapi/clinic/file.c.h +++ b/Modules/_testcapi/clinic/file.c.h @@ -61,4 +61,38 @@ _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=e943bbd7f181d079 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_testcapi_py_universalnewlinefgets__doc__, +"py_universalnewlinefgets($module, file, size, /)\n" +"--\n" +"\n" +"Read a line from a file using Py_UniversalNewlineFgets."); + +#define _TESTCAPI_PY_UNIVERSALNEWLINEFGETS_METHODDEF \ + {"py_universalnewlinefgets", _PyCFunction_CAST(_testcapi_py_universalnewlinefgets), METH_FASTCALL, _testcapi_py_universalnewlinefgets__doc__}, + +static PyObject * +_testcapi_py_universalnewlinefgets_impl(PyObject *module, PyObject *file, + int size); + +static PyObject * +_testcapi_py_universalnewlinefgets(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *file; + int size; + + if (!_PyArg_CheckPositional("py_universalnewlinefgets", nargs, 2, 2)) { + goto exit; + } + file = args[0]; + size = PyLong_AsInt(args[1]); + if (size == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _testcapi_py_universalnewlinefgets_impl(module, file, size); + +exit: + return return_value; +} +/*[clinic end generated code: output=a5ed111054e3a0bc input=a9049054013a1b77]*/ diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c index 060e0f50598d7e..f70cd99a3e1268 100644 --- a/Modules/_testcapi/file.c +++ b/Modules/_testcapi/file.c @@ -4,6 +4,8 @@ #include "parts.h" #include "util.h" #include "clinic/file.c.h" +#include +#include /*[clinic input] @@ -57,9 +59,61 @@ _testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode, } +/*[clinic input] +_testcapi.py_universalnewlinefgets + + file: object + size: int + / + +Read a line from a file using Py_UniversalNewlineFgets. +[clinic start generated code]*/ + +static PyObject * +_testcapi_py_universalnewlinefgets_impl(PyObject *module, PyObject *file, + int size) +/*[clinic end generated code: output=2ce1bc76c9dc871c input=02c236049d18569a]*/ +{ + int fd = PyObject_AsFileDescriptor(file); + if (fd == -1) { + return NULL; + } + + FILE *fp; +#ifdef MS_WINDOWS + fp = _fdopen(fd, "rb"); +#else + fp = fdopen(fd, "rb"); +#endif + + if (fp == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + char *buf = (char *)PyMem_Malloc(size); + if (buf == NULL) { + fclose(fp); + return PyErr_NoMemory(); + } + + char *result = Py_UniversalNewlineFgets(buf, size, fp, NULL); + if (result == NULL) { + PyMem_Free(buf); + fclose(fp); + Py_RETURN_NONE; + } + + PyObject *line = PyBytes_FromString(result); + PyMem_Free(buf); + + return line; +} + static PyMethodDef test_methods[] = { _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF _TESTCAPI_PY_FOPEN_METHODDEF + _TESTCAPI_PY_UNIVERSALNEWLINEFGETS_METHODDEF {NULL}, };