From f30fd64291d11117511197c8b96ad68718318e15 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 22 Aug 2026 11:30:12 +0300 Subject: [PATCH 1/3] gh-155496: Use Argument Clinic for the getpath module --- Modules/clinic/getpath.c.h | 337 +++++++++++++++++++++++++++++++++++++ Modules/getpath.c | 207 +++++++++++++++-------- 2 files changed, 477 insertions(+), 67 deletions(-) create mode 100644 Modules/clinic/getpath.c.h diff --git a/Modules/clinic/getpath.c.h b/Modules/clinic/getpath.c.h new file mode 100644 index 000000000000000..c1e44aed73b5f63 --- /dev/null +++ b/Modules/clinic/getpath.c.h @@ -0,0 +1,337 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_modsupport.h" // _PyArg_BadArgument() + +PyDoc_STRVAR(getpath_abspath__doc__, +"abspath($module, path, /)\n" +"--\n" +"\n" +"Return the absolute path."); + +#define GETPATH_ABSPATH_METHODDEF \ + {"abspath", (PyCFunction)getpath_abspath, METH_O, getpath_abspath__doc__}, + +static PyObject * +getpath_abspath_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_abspath(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("abspath", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_abspath_impl(module, pathobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_basename__doc__, +"basename($module, path, /)\n" +"--\n" +"\n" +"Return the final component of the path."); + +#define GETPATH_BASENAME_METHODDEF \ + {"basename", (PyCFunction)getpath_basename, METH_O, getpath_basename__doc__}, + +static PyObject * +getpath_basename_impl(PyObject *module, PyObject *path); + +static PyObject * +getpath_basename(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *path; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("basename", "argument", "str", arg); + goto exit; + } + path = arg; + return_value = getpath_basename_impl(module, path); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_dirname__doc__, +"dirname($module, path, /)\n" +"--\n" +"\n" +"Return the directory component of the path."); + +#define GETPATH_DIRNAME_METHODDEF \ + {"dirname", (PyCFunction)getpath_dirname, METH_O, getpath_dirname__doc__}, + +static PyObject * +getpath_dirname_impl(PyObject *module, PyObject *path); + +static PyObject * +getpath_dirname(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *path; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("dirname", "argument", "str", arg); + goto exit; + } + path = arg; + return_value = getpath_dirname_impl(module, path); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_isabs__doc__, +"isabs($module, path, /)\n" +"--\n" +"\n" +"Return True if the path is absolute."); + +#define GETPATH_ISABS_METHODDEF \ + {"isabs", (PyCFunction)getpath_isabs, METH_O, getpath_isabs__doc__}, + +static PyObject * +getpath_isabs_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_isabs(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("isabs", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_isabs_impl(module, pathobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_hassuffix__doc__, +"hassuffix($module, path, suffix, /)\n" +"--\n" +"\n" +"Return True if the path ends with the suffix, ignoring the case."); + +#define GETPATH_HASSUFFIX_METHODDEF \ + {"hassuffix", _PyCFunction_CAST(getpath_hassuffix), METH_FASTCALL, getpath_hassuffix__doc__}, + +static PyObject * +getpath_hassuffix_impl(PyObject *module, PyObject *pathobj, + PyObject *suffixobj); + +static PyObject * +getpath_hassuffix(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + PyObject *suffixobj; + + if (!_PyArg_CheckPositional("hassuffix", nargs, 2, 2)) { + goto exit; + } + if (!PyUnicode_Check(args[0])) { + _PyArg_BadArgument("hassuffix", "argument 1", "str", args[0]); + goto exit; + } + pathobj = args[0]; + if (!PyUnicode_Check(args[1])) { + _PyArg_BadArgument("hassuffix", "argument 2", "str", args[1]); + goto exit; + } + suffixobj = args[1]; + return_value = getpath_hassuffix_impl(module, pathobj, suffixobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_isdir__doc__, +"isdir($module, path, /)\n" +"--\n" +"\n" +"Return True if the path is a directory."); + +#define GETPATH_ISDIR_METHODDEF \ + {"isdir", (PyCFunction)getpath_isdir, METH_O, getpath_isdir__doc__}, + +static PyObject * +getpath_isdir_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_isdir(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("isdir", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_isdir_impl(module, pathobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_isfile__doc__, +"isfile($module, path, /)\n" +"--\n" +"\n" +"Return True if the path is a regular file."); + +#define GETPATH_ISFILE_METHODDEF \ + {"isfile", (PyCFunction)getpath_isfile, METH_O, getpath_isfile__doc__}, + +static PyObject * +getpath_isfile_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_isfile(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("isfile", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_isfile_impl(module, pathobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_isxfile__doc__, +"isxfile($module, path, /)\n" +"--\n" +"\n" +"Return True if the path is an executable file."); + +#define GETPATH_ISXFILE_METHODDEF \ + {"isxfile", (PyCFunction)getpath_isxfile, METH_O, getpath_isxfile__doc__}, + +static PyObject * +getpath_isxfile_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_isxfile(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("isxfile", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_isxfile_impl(module, pathobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_joinpath__doc__, +"joinpath($module, /, *args)\n" +"--\n" +"\n" +"Join the path components."); + +#define GETPATH_JOINPATH_METHODDEF \ + {"joinpath", _PyCFunction_CAST(getpath_joinpath), METH_FASTCALL, getpath_joinpath__doc__}, + +static PyObject * +getpath_joinpath_impl(PyObject *module, PyObject *args); + +static PyObject * +getpath_joinpath(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *__clinic_args = NULL; + + __clinic_args = PyTuple_FromArray(args, nargs); + if (__clinic_args == NULL) { + goto exit; + } + return_value = getpath_joinpath_impl(module, __clinic_args); + +exit: + /* Cleanup for args */ + Py_XDECREF(__clinic_args); + + return return_value; +} + +PyDoc_STRVAR(getpath_readlines__doc__, +"readlines($module, path, /)\n" +"--\n" +"\n" +"Return the lines of the file."); + +#define GETPATH_READLINES_METHODDEF \ + {"readlines", (PyCFunction)getpath_readlines, METH_O, getpath_readlines__doc__}, + +static PyObject * +getpath_readlines_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_readlines(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("readlines", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_readlines_impl(module, pathobj); + +exit: + return return_value; +} + +PyDoc_STRVAR(getpath_realpath__doc__, +"realpath($module, path, /)\n" +"--\n" +"\n" +"Resolve a symlinked file."); + +#define GETPATH_REALPATH_METHODDEF \ + {"realpath", (PyCFunction)getpath_realpath, METH_O, getpath_realpath__doc__}, + +static PyObject * +getpath_realpath_impl(PyObject *module, PyObject *pathobj); + +static PyObject * +getpath_realpath(PyObject *module, PyObject *arg) +{ + PyObject *return_value = NULL; + PyObject *pathobj; + + if (!PyUnicode_Check(arg)) { + _PyArg_BadArgument("realpath", "argument", "str", arg); + goto exit; + } + pathobj = arg; + return_value = getpath_realpath_impl(module, pathobj); + +exit: + return return_value; +} +/*[clinic end generated code: output=766f73e4b40e9192 input=a9049054013a1b77]*/ diff --git a/Modules/getpath.c b/Modules/getpath.c index ed41536acbcc813..6a24018d36414e2 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -58,17 +58,30 @@ #endif +/*[clinic input] +module getpath +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b425aa3ea7292100]*/ + +#include "clinic/getpath.c.h" + /* HELPER FUNCTIONS for getpath.py */ +/*[clinic input] +getpath.abspath + + path as pathobj: unicode + / + +Return the absolute path. +[clinic start generated code]*/ + static PyObject * -getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args) +getpath_abspath_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=a307fc0d5d82eb2e input=2f8179427a1a31a6]*/ { PyObject *r = NULL; - PyObject *pathobj; wchar_t *path; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } Py_ssize_t len; path = PyUnicode_AsWideCharString(pathobj, &len); if (path) { @@ -85,13 +98,19 @@ getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.basename + + path: unicode + / + +Return the final component of the path. +[clinic start generated code]*/ + static PyObject * -getpath_basename(PyObject *Py_UNUSED(self), PyObject *args) +getpath_basename_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=d383d3b34842236e input=0de5b71fc78bbad8]*/ { - PyObject *path; - if (!PyArg_ParseTuple(args, "U", &path)) { - return NULL; - } Py_ssize_t end = PyUnicode_GET_LENGTH(path); Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); if (pos < 0) { @@ -101,13 +120,19 @@ getpath_basename(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.dirname + + path: unicode + / + +Return the directory component of the path. +[clinic start generated code]*/ + static PyObject * -getpath_dirname(PyObject *Py_UNUSED(self), PyObject *args) +getpath_dirname_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=6887a025f5f6784c input=0c33269fe28957fb]*/ { - PyObject *path; - if (!PyArg_ParseTuple(args, "U", &path)) { - return NULL; - } Py_ssize_t end = PyUnicode_GET_LENGTH(path); Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); if (pos < 0) { @@ -117,15 +142,21 @@ getpath_dirname(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.isabs + + path as pathobj: unicode + / + +Return True if the path is absolute. +[clinic start generated code]*/ + static PyObject * -getpath_isabs(PyObject *Py_UNUSED(self), PyObject *args) +getpath_isabs_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=7f5c62037d0b2c86 input=e49d90a186348140]*/ { PyObject *r = NULL; - PyObject *pathobj; const wchar_t *path; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } path = PyUnicode_AsWideCharString(pathobj, NULL); if (path) { r = _Py_isabs(path) ? Py_True : Py_False; @@ -135,17 +166,24 @@ getpath_isabs(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.hassuffix + + path as pathobj: unicode + suffix as suffixobj: unicode + / + +Return True if the path ends with the suffix, ignoring the case. +[clinic start generated code]*/ + static PyObject * -getpath_hassuffix(PyObject *Py_UNUSED(self), PyObject *args) +getpath_hassuffix_impl(PyObject *module, PyObject *pathobj, + PyObject *suffixobj) +/*[clinic end generated code: output=40e52a2ebe497e47 input=dffaf0ddc9d63647]*/ { PyObject *r = NULL; - PyObject *pathobj; - PyObject *suffixobj; const wchar_t *path; const wchar_t *suffix; - if (!PyArg_ParseTuple(args, "UU", &pathobj, &suffixobj)) { - return NULL; - } Py_ssize_t len, suffixLen; path = PyUnicode_AsWideCharString(pathobj, &len); if (path) { @@ -170,15 +208,21 @@ getpath_hassuffix(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.isdir + + path as pathobj: unicode + / + +Return True if the path is a directory. +[clinic start generated code]*/ + static PyObject * -getpath_isdir(PyObject *Py_UNUSED(self), PyObject *args) +getpath_isdir_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=5af217f29e968e1b input=d1d4c4d74d2e37f1]*/ { PyObject *r = NULL; - PyObject *pathobj; const wchar_t *path; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } path = PyUnicode_AsWideCharString(pathobj, NULL); if (path) { #ifdef MS_WINDOWS @@ -195,13 +239,19 @@ getpath_isdir(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.isfile + + path as pathobj: unicode + / + +Return True if the path is a regular file. +[clinic start generated code]*/ + static PyObject * -getpath_isfile(PyObject *Py_UNUSED(self), PyObject *args) +getpath_isfile_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=0060d782bd507740 input=71831c61d25e3f9e]*/ { - PyObject *pathobj; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } int isfile; #ifdef MS_WINDOWS @@ -226,13 +276,19 @@ getpath_isfile(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.isxfile + + path as pathobj: unicode + / + +Return True if the path is an executable file. +[clinic start generated code]*/ + static PyObject * -getpath_isxfile(PyObject *Py_UNUSED(self), PyObject *args) +getpath_isxfile_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=67348f84968ac985 input=4b272be4846d89a2]*/ { - PyObject *pathobj; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } int isxfile; #ifdef MS_WINDOWS @@ -262,13 +318,18 @@ getpath_isxfile(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.joinpath + + *args: tuple + +Join the path components. +[clinic start generated code]*/ + static PyObject * -getpath_joinpath(PyObject *Py_UNUSED(self), PyObject *args) +getpath_joinpath_impl(PyObject *module, PyObject *args) +/*[clinic end generated code: output=0044d429b32e5f5a input=5c67eeca4a6d1413]*/ { - if (!PyTuple_Check(args)) { - PyErr_SetString(PyExc_TypeError, "requires tuple of arguments"); - return NULL; - } Py_ssize_t n = PyTuple_GET_SIZE(args); if (n == 0) { return Py_GetConstant(Py_CONSTANT_EMPTY_STR); @@ -347,13 +408,19 @@ getpath_joinpath(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.readlines + + path as pathobj: unicode + / + +Return the lines of the file. +[clinic start generated code]*/ + static PyObject * -getpath_readlines(PyObject *Py_UNUSED(self), PyObject *args) +getpath_readlines_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=6a7c1a1d2bb23046 input=6f54150f72789b78]*/ { - PyObject *pathobj; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } FILE *fp = Py_fopen(pathobj, "rb"); if (!fp) { return NULL; @@ -423,13 +490,19 @@ getpath_readlines(PyObject *Py_UNUSED(self), PyObject *args) } +/*[clinic input] +getpath.realpath + + path as pathobj: unicode + / + +Resolve a symlinked file. +[clinic start generated code]*/ + static PyObject * -getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args) +getpath_realpath_impl(PyObject *module, PyObject *pathobj) +/*[clinic end generated code: output=8559260e3786874e input=d3c5eee0ddd2c3e2]*/ { - PyObject *pathobj; - if (!PyArg_ParseTuple(args, "U", &pathobj)) { - return NULL; - } #if defined(HAVE_READLINK) /* This readlink calculation only resolves a symlinked file, and does not resolve any path segments. This is consistent with @@ -566,17 +639,17 @@ getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args) static PyMethodDef getpath_methods[] = { - {"abspath", getpath_abspath, METH_VARARGS, NULL}, - {"basename", getpath_basename, METH_VARARGS, NULL}, - {"dirname", getpath_dirname, METH_VARARGS, NULL}, - {"hassuffix", getpath_hassuffix, METH_VARARGS, NULL}, - {"isabs", getpath_isabs, METH_VARARGS, NULL}, - {"isdir", getpath_isdir, METH_VARARGS, NULL}, - {"isfile", getpath_isfile, METH_VARARGS, NULL}, - {"isxfile", getpath_isxfile, METH_VARARGS, NULL}, - {"joinpath", getpath_joinpath, METH_VARARGS, NULL}, - {"readlines", getpath_readlines, METH_VARARGS, NULL}, - {"realpath", getpath_realpath, METH_VARARGS, NULL}, + GETPATH_ABSPATH_METHODDEF + GETPATH_BASENAME_METHODDEF + GETPATH_DIRNAME_METHODDEF + GETPATH_HASSUFFIX_METHODDEF + GETPATH_ISABS_METHODDEF + GETPATH_ISDIR_METHODDEF + GETPATH_ISFILE_METHODDEF + GETPATH_ISXFILE_METHODDEF + GETPATH_JOINPATH_METHODDEF + GETPATH_READLINES_METHODDEF + GETPATH_REALPATH_METHODDEF {NULL, NULL, 0, NULL} }; From c7a7b19039f9018a5c98d109715ad5bba9960016 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 26 Sep 2026 12:18:43 +0300 Subject: [PATCH 2/3] gh-155496: Use the Py_UNICODE converter in the getpath module Argument Clinic now converts the path argument of abspath(), isabs() and isdir() to a wide character string, and isabs() and isdir() use the bool return converter. --- Modules/clinic/getpath.c.h | 58 +++++++++++++++++++++++-------- Modules/getpath.c | 71 ++++++++++++++------------------------ 2 files changed, 68 insertions(+), 61 deletions(-) diff --git a/Modules/clinic/getpath.c.h b/Modules/clinic/getpath.c.h index c1e44aed73b5f63..2b009d3f54d2fc3 100644 --- a/Modules/clinic/getpath.c.h +++ b/Modules/clinic/getpath.c.h @@ -14,22 +14,28 @@ PyDoc_STRVAR(getpath_abspath__doc__, {"abspath", (PyCFunction)getpath_abspath, METH_O, getpath_abspath__doc__}, static PyObject * -getpath_abspath_impl(PyObject *module, PyObject *pathobj); +getpath_abspath_impl(PyObject *module, const wchar_t *path); static PyObject * getpath_abspath(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *pathobj; + const wchar_t *path = NULL; if (!PyUnicode_Check(arg)) { _PyArg_BadArgument("abspath", "argument", "str", arg); goto exit; } - pathobj = arg; - return_value = getpath_abspath_impl(module, pathobj); + path = PyUnicode_AsWideCharString(arg, NULL); + if (path == NULL) { + goto exit; + } + return_value = getpath_abspath_impl(module, path); exit: + /* Cleanup for path */ + PyMem_Free((void *)path); + return return_value; } @@ -100,23 +106,34 @@ PyDoc_STRVAR(getpath_isabs__doc__, #define GETPATH_ISABS_METHODDEF \ {"isabs", (PyCFunction)getpath_isabs, METH_O, getpath_isabs__doc__}, -static PyObject * -getpath_isabs_impl(PyObject *module, PyObject *pathobj); +static int +getpath_isabs_impl(PyObject *module, const wchar_t *path); static PyObject * getpath_isabs(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *pathobj; + const wchar_t *path = NULL; + int _return_value; if (!PyUnicode_Check(arg)) { _PyArg_BadArgument("isabs", "argument", "str", arg); goto exit; } - pathobj = arg; - return_value = getpath_isabs_impl(module, pathobj); + path = PyUnicode_AsWideCharString(arg, NULL); + if (path == NULL) { + goto exit; + } + _return_value = getpath_isabs_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: + /* Cleanup for path */ + PyMem_Free((void *)path); + return return_value; } @@ -168,23 +185,34 @@ PyDoc_STRVAR(getpath_isdir__doc__, #define GETPATH_ISDIR_METHODDEF \ {"isdir", (PyCFunction)getpath_isdir, METH_O, getpath_isdir__doc__}, -static PyObject * -getpath_isdir_impl(PyObject *module, PyObject *pathobj); +static int +getpath_isdir_impl(PyObject *module, const wchar_t *path); static PyObject * getpath_isdir(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; - PyObject *pathobj; + const wchar_t *path = NULL; + int _return_value; if (!PyUnicode_Check(arg)) { _PyArg_BadArgument("isdir", "argument", "str", arg); goto exit; } - pathobj = arg; - return_value = getpath_isdir_impl(module, pathobj); + path = PyUnicode_AsWideCharString(arg, NULL); + if (path == NULL) { + goto exit; + } + _return_value = getpath_isdir_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: + /* Cleanup for path */ + PyMem_Free((void *)path); + return return_value; } @@ -334,4 +362,4 @@ getpath_realpath(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=766f73e4b40e9192 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=176f5c505fa66eff input=a9049054013a1b77]*/ diff --git a/Modules/getpath.c b/Modules/getpath.c index 6a24018d36414e2..c9fb26d0240cbe7 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -70,30 +70,23 @@ module getpath /*[clinic input] getpath.abspath - path as pathobj: unicode + path: Py_UNICODE / Return the absolute path. [clinic start generated code]*/ static PyObject * -getpath_abspath_impl(PyObject *module, PyObject *pathobj) -/*[clinic end generated code: output=a307fc0d5d82eb2e input=2f8179427a1a31a6]*/ +getpath_abspath_impl(PyObject *module, const wchar_t *path) +/*[clinic end generated code: output=5009336e32e75b9b input=0fd554ce535744f2]*/ { - PyObject *r = NULL; - wchar_t *path; - Py_ssize_t len; - path = PyUnicode_AsWideCharString(pathobj, &len); - if (path) { - wchar_t *abs; - if (_Py_abspath((const wchar_t *)_Py_normpath(path, -1), &abs) == 0 && abs) { - r = PyUnicode_FromWideChar(abs, -1); - PyMem_RawFree((void *)abs); - } else { - PyErr_SetString(PyExc_OSError, "failed to make path absolute"); - } - PyMem_Free((void *)path); + wchar_t *abs; + if (_Py_abspath(_Py_normpath((wchar_t *)path, -1), &abs) < 0 || abs == NULL) { + PyErr_SetString(PyExc_OSError, "failed to make path absolute"); + return NULL; } + PyObject *r = PyUnicode_FromWideChar(abs, -1); + PyMem_RawFree((void *)abs); return r; } @@ -143,26 +136,19 @@ getpath_dirname_impl(PyObject *module, PyObject *path) /*[clinic input] -getpath.isabs +getpath.isabs -> bool - path as pathobj: unicode + path: Py_UNICODE / Return True if the path is absolute. [clinic start generated code]*/ -static PyObject * -getpath_isabs_impl(PyObject *module, PyObject *pathobj) -/*[clinic end generated code: output=7f5c62037d0b2c86 input=e49d90a186348140]*/ +static int +getpath_isabs_impl(PyObject *module, const wchar_t *path) +/*[clinic end generated code: output=4659b9e6f088c2ca input=04931a206cdd77f6]*/ { - PyObject *r = NULL; - const wchar_t *path; - path = PyUnicode_AsWideCharString(pathobj, NULL); - if (path) { - r = _Py_isabs(path) ? Py_True : Py_False; - PyMem_Free((void *)path); - } - return Py_XNewRef(r); + return _Py_isabs(path); } @@ -209,33 +195,26 @@ getpath_hassuffix_impl(PyObject *module, PyObject *pathobj, /*[clinic input] -getpath.isdir +getpath.isdir -> bool - path as pathobj: unicode + path: Py_UNICODE / Return True if the path is a directory. [clinic start generated code]*/ -static PyObject * -getpath_isdir_impl(PyObject *module, PyObject *pathobj) -/*[clinic end generated code: output=5af217f29e968e1b input=d1d4c4d74d2e37f1]*/ +static int +getpath_isdir_impl(PyObject *module, const wchar_t *path) +/*[clinic end generated code: output=467820dd83daa7a6 input=0ddd588cd5357a9a]*/ { - PyObject *r = NULL; - const wchar_t *path; - path = PyUnicode_AsWideCharString(pathobj, NULL); - if (path) { #ifdef MS_WINDOWS - DWORD attr = GetFileAttributesW(path); - r = (attr != INVALID_FILE_ATTRIBUTES) && - (attr & FILE_ATTRIBUTE_DIRECTORY) ? Py_True : Py_False; + DWORD attr = GetFileAttributesW(path); + return (attr != INVALID_FILE_ATTRIBUTES) + && (attr & FILE_ATTRIBUTE_DIRECTORY); #else - struct stat st; - r = (_Py_wstat(path, &st) == 0) && S_ISDIR(st.st_mode) ? Py_True : Py_False; + struct stat st; + return (_Py_wstat(path, &st) == 0) && S_ISDIR(st.st_mode); #endif - PyMem_Free((void *)path); - } - return Py_XNewRef(r); } From eb6990cecf71843a1af0b1438fa062dec419d2b9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Sep 2026 13:31:21 +0300 Subject: [PATCH 3/3] gh-155496: Use the bool return converter in the getpath module hassuffix(), isfile() and isxfile() now use the bool return converter, as isabs() and isdir() already did. This also fixes a use after free in isxfile() on Windows: the path was freed before being passed to CompareStringOrdinal(). --- Modules/clinic/getpath.c.h | 29 ++++++++++++---- Modules/getpath.c | 71 ++++++++++++++++---------------------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/Modules/clinic/getpath.c.h b/Modules/clinic/getpath.c.h index 2b009d3f54d2fc3..54ab0240e4482c5 100644 --- a/Modules/clinic/getpath.c.h +++ b/Modules/clinic/getpath.c.h @@ -146,7 +146,7 @@ PyDoc_STRVAR(getpath_hassuffix__doc__, #define GETPATH_HASSUFFIX_METHODDEF \ {"hassuffix", _PyCFunction_CAST(getpath_hassuffix), METH_FASTCALL, getpath_hassuffix__doc__}, -static PyObject * +static int getpath_hassuffix_impl(PyObject *module, PyObject *pathobj, PyObject *suffixobj); @@ -156,6 +156,7 @@ getpath_hassuffix(PyObject *module, PyObject *const *args, Py_ssize_t nargs) PyObject *return_value = NULL; PyObject *pathobj; PyObject *suffixobj; + int _return_value; if (!_PyArg_CheckPositional("hassuffix", nargs, 2, 2)) { goto exit; @@ -170,7 +171,11 @@ getpath_hassuffix(PyObject *module, PyObject *const *args, Py_ssize_t nargs) goto exit; } suffixobj = args[1]; - return_value = getpath_hassuffix_impl(module, pathobj, suffixobj); + _return_value = getpath_hassuffix_impl(module, pathobj, suffixobj); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -225,7 +230,7 @@ PyDoc_STRVAR(getpath_isfile__doc__, #define GETPATH_ISFILE_METHODDEF \ {"isfile", (PyCFunction)getpath_isfile, METH_O, getpath_isfile__doc__}, -static PyObject * +static int getpath_isfile_impl(PyObject *module, PyObject *pathobj); static PyObject * @@ -233,13 +238,18 @@ getpath_isfile(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; PyObject *pathobj; + int _return_value; if (!PyUnicode_Check(arg)) { _PyArg_BadArgument("isfile", "argument", "str", arg); goto exit; } pathobj = arg; - return_value = getpath_isfile_impl(module, pathobj); + _return_value = getpath_isfile_impl(module, pathobj); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -254,7 +264,7 @@ PyDoc_STRVAR(getpath_isxfile__doc__, #define GETPATH_ISXFILE_METHODDEF \ {"isxfile", (PyCFunction)getpath_isxfile, METH_O, getpath_isxfile__doc__}, -static PyObject * +static int getpath_isxfile_impl(PyObject *module, PyObject *pathobj); static PyObject * @@ -262,13 +272,18 @@ getpath_isxfile(PyObject *module, PyObject *arg) { PyObject *return_value = NULL; PyObject *pathobj; + int _return_value; if (!PyUnicode_Check(arg)) { _PyArg_BadArgument("isxfile", "argument", "str", arg); goto exit; } pathobj = arg; - return_value = getpath_isxfile_impl(module, pathobj); + _return_value = getpath_isxfile_impl(module, pathobj); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -362,4 +377,4 @@ getpath_realpath(PyObject *module, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=176f5c505fa66eff input=a9049054013a1b77]*/ +/*[clinic end generated code: output=74266cdd2d87e28d input=a9049054013a1b77]*/ diff --git a/Modules/getpath.c b/Modules/getpath.c index c9fb26d0240cbe7..48047de0c12af2d 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -153,7 +153,7 @@ getpath_isabs_impl(PyObject *module, const wchar_t *path) /*[clinic input] -getpath.hassuffix +getpath.hassuffix -> bool path as pathobj: unicode suffix as suffixobj: unicode @@ -162,30 +162,24 @@ getpath.hassuffix Return True if the path ends with the suffix, ignoring the case. [clinic start generated code]*/ -static PyObject * +static int getpath_hassuffix_impl(PyObject *module, PyObject *pathobj, PyObject *suffixobj) -/*[clinic end generated code: output=40e52a2ebe497e47 input=dffaf0ddc9d63647]*/ +/*[clinic end generated code: output=c4cbd6f2f567f83c input=4b477740575b94d8]*/ { - PyObject *r = NULL; - const wchar_t *path; - const wchar_t *suffix; + int r = -1; Py_ssize_t len, suffixLen; - path = PyUnicode_AsWideCharString(pathobj, &len); + const wchar_t *path = PyUnicode_AsWideCharString(pathobj, &len); if (path) { - suffix = PyUnicode_AsWideCharString(suffixobj, &suffixLen); + const wchar_t *suffix = PyUnicode_AsWideCharString(suffixobj, + &suffixLen); if (suffix) { - if (suffixLen > len || + r = suffixLen <= len && #ifdef MS_WINDOWS - wcsicmp(&path[len - suffixLen], suffix) != 0 + wcsicmp(&path[len - suffixLen], suffix) == 0; #else - wcscmp(&path[len - suffixLen], suffix) != 0 + wcscmp(&path[len - suffixLen], suffix) == 0; #endif - ) { - r = Py_NewRef(Py_False); - } else { - r = Py_NewRef(Py_True); - } PyMem_Free((void *)suffix); } PyMem_Free((void *)path); @@ -219,7 +213,7 @@ getpath_isdir_impl(PyObject *module, const wchar_t *path) /*[clinic input] -getpath.isfile +getpath.isfile -> bool path as pathobj: unicode / @@ -227,36 +221,33 @@ getpath.isfile Return True if the path is a regular file. [clinic start generated code]*/ -static PyObject * +static int getpath_isfile_impl(PyObject *module, PyObject *pathobj) -/*[clinic end generated code: output=0060d782bd507740 input=71831c61d25e3f9e]*/ +/*[clinic end generated code: output=197acb7a5434b9d1 input=ba1c2746ec8f2f47]*/ { - - int isfile; #ifdef MS_WINDOWS wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL); if (path == NULL) { - return NULL; + return -1; } DWORD attr = GetFileAttributesW(path); PyMem_Free(path); - isfile = ((attr != INVALID_FILE_ATTRIBUTES) - && !(attr & FILE_ATTRIBUTE_DIRECTORY)); + return (attr != INVALID_FILE_ATTRIBUTES) + && !(attr & FILE_ATTRIBUTE_DIRECTORY); #else struct stat st; int res = _Py_stat(pathobj, &st); if (res == -2) { - return NULL; + return -1; } - isfile = ((res == 0) && S_ISREG(st.st_mode)); + return (res == 0) && S_ISREG(st.st_mode); #endif - return PyBool_FromLong(isfile); } /*[clinic input] -getpath.isxfile +getpath.isxfile -> bool path as pathobj: unicode / @@ -264,36 +255,32 @@ getpath.isxfile Return True if the path is an executable file. [clinic start generated code]*/ -static PyObject * +static int getpath_isxfile_impl(PyObject *module, PyObject *pathobj) -/*[clinic end generated code: output=67348f84968ac985 input=4b272be4846d89a2]*/ +/*[clinic end generated code: output=2f0bc6708d945bc8 input=6d99faa90e262511]*/ { - - int isxfile; #ifdef MS_WINDOWS Py_ssize_t cchPath; wchar_t *path = PyUnicode_AsWideCharString(pathobj, &cchPath); if (path == NULL) { - return NULL; + return -1; } DWORD attr = GetFileAttributesW(path); + int isxfile = (attr != INVALID_FILE_ATTRIBUTES) && + !(attr & FILE_ATTRIBUTE_DIRECTORY) && + (cchPath >= 4) && + (CompareStringOrdinal(path + cchPath - 4, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL); PyMem_Free(path); - isxfile = (attr != INVALID_FILE_ATTRIBUTES) && - !(attr & FILE_ATTRIBUTE_DIRECTORY) && - (cchPath >= 4) && - (CompareStringOrdinal(path + cchPath - 4, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL); + return isxfile; #else struct stat st; int res = _Py_stat(pathobj, &st); if (res == -2) { - return NULL; + return -1; } - isxfile = ((res == 0) - && S_ISREG(st.st_mode) - && (st.st_mode & 0111)); + return (res == 0) && S_ISREG(st.st_mode) && (st.st_mode & 0111); #endif - return PyBool_FromLong(isxfile); }