From d4028724f2c8c674202615b772913765423c69fd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 14 Mar 2024 10:28:58 +0100 Subject: [PATCH] gh-116646: Add limited C API support to AC fildes converter (#116769) Add tests on the "fildes" converter to _testclinic_limited. --- Lib/test/test_clinic.py | 33 ++++++++++++++++++++++++ Modules/_testclinic_limited.c | 18 +++++++++++++ Modules/clinic/_testclinic_limited.c.h | 35 +++++++++++++++++++++++++- Tools/clinic/clinic.py | 25 +++++++++--------- 4 files changed, 98 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index cf3eeaadf00..a60f087ef28 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3698,6 +3698,39 @@ class LimitedCAPIFunctionalTest(unittest.TestCase): with self.assertRaises(TypeError): func(1., "2") + def test_get_file_descriptor(self): + # test 'file descriptor' converter: call PyObject_AsFileDescriptor() + get_fd = _testclinic_limited.get_file_descriptor + + class MyInt(int): + pass + + class MyFile: + def __init__(self, fd): + self._fd = fd + def fileno(self): + return self._fd + + for fd in (0, 1, 2, 5, 123_456): + self.assertEqual(get_fd(fd), fd) + + myint = MyInt(fd) + self.assertEqual(get_fd(myint), fd) + + myfile = MyFile(fd) + self.assertEqual(get_fd(myfile), fd) + + with self.assertRaises(OverflowError): + get_fd(2**256) + with self.assertWarnsRegex(RuntimeWarning, + "bool is used as a file descriptor"): + get_fd(True) + with self.assertRaises(TypeError): + get_fd(1.0) + with self.assertRaises(TypeError): + get_fd("abc") + with self.assertRaises(TypeError): + get_fd(None) class PermutationTests(unittest.TestCase): diff --git a/Modules/_testclinic_limited.c b/Modules/_testclinic_limited.c index 1a73c04aecb..29f1b7c13e4 100644 --- a/Modules/_testclinic_limited.c +++ b/Modules/_testclinic_limited.c @@ -105,12 +105,30 @@ my_double_sum_impl(PyObject *module, double x, double y) } +/*[clinic input] +get_file_descriptor -> int + + file as fd: fildes + / + +Get a file descriptor. +[clinic start generated code]*/ + +static int +get_file_descriptor_impl(PyObject *module, int fd) +/*[clinic end generated code: output=80051ebad54db8a8 input=82e2a1418848cd5b]*/ +{ + return fd; +} + + static PyMethodDef tester_methods[] = { TEST_EMPTY_FUNCTION_METHODDEF MY_INT_FUNC_METHODDEF MY_INT_SUM_METHODDEF MY_FLOAT_SUM_METHODDEF MY_DOUBLE_SUM_METHODDEF + GET_FILE_DESCRIPTOR_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_testclinic_limited.c.h b/Modules/clinic/_testclinic_limited.c.h index 690e782b839..94897f4c6dc 100644 --- a/Modules/clinic/_testclinic_limited.c.h +++ b/Modules/clinic/_testclinic_limited.c.h @@ -173,4 +173,37 @@ my_double_sum(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=bb9f6b8c5d9e6a79 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(get_file_descriptor__doc__, +"get_file_descriptor($module, file, /)\n" +"--\n" +"\n" +"Get a file descriptor."); + +#define GET_FILE_DESCRIPTOR_METHODDEF \ + {"get_file_descriptor", (PyCFunction)get_file_descriptor, METH_O, get_file_descriptor__doc__}, + +static int +get_file_descriptor_impl(PyObject *module, int fd); + +static PyObject * +get_file_descriptor(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + int fd; + int _return_value; + + fd = PyObject_AsFileDescriptor(arg); + if (fd < 0) { + goto exit; + } + _return_value = get_file_descriptor_impl(module, fd); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromLong((long)_return_value); + +exit: + return return_value; +} +/*[clinic end generated code: output=03fd7811c056dc74 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4c7c4dca37c..c81af5e696e 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -3800,18 +3800,19 @@ class fildes_converter(CConverter): type = 'int' converter = '_PyLong_FileDescriptor_Converter' - def converter_init(self, *, accept: TypeSet = {int, NoneType}) -> None: - self.add_include('pycore_fileutils.h', - '_PyLong_FileDescriptor_Converter()') - - def _parse_arg(self, argname: str, displayname: str) -> str | None: - return self.format_code(""" - {paramname} = PyObject_AsFileDescriptor({argname}); - if ({paramname} == -1) {{{{ - goto exit; - }}}} - """, - argname=argname) + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if limited_capi: + return self.format_code(""" + {paramname} = PyObject_AsFileDescriptor({argname}); + if ({paramname} < 0) {{{{ + goto exit; + }}}} + """, + argname=argname) + else: + self.add_include('pycore_fileutils.h', + '_PyLong_FileDescriptor_Converter()') + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class float_converter(CConverter):