https://github.com/python/cpython/commit/99cdf1deb61852dda9f8df639b0f13c1ce121cfa
commit: 99cdf1deb61852dda9f8df639b0f13c1ce121cfa
branch: main
author: sobolevn <m...@sobolevn.me>
committer: sobolevn <m...@sobolevn.me>
date: 2025-07-23T14:56:02+03:00
summary:

gh-136437: Make several functions in `os.path` pos-only (#136949)

files:
M Lib/genericpath.py
M Lib/ntpath.py
M Lib/posixpath.py
M Modules/clinic/posixmodule.c.h
M Modules/posixmodule.c

diff --git a/Lib/genericpath.py b/Lib/genericpath.py
index 9363f564aab7a6..4a223654994194 100644
--- a/Lib/genericpath.py
+++ b/Lib/genericpath.py
@@ -81,28 +81,28 @@ def isdevdrive(path):
     return False
 
 
-def getsize(filename):
+def getsize(filename, /):
     """Return the size of a file, reported by os.stat()."""
     return os.stat(filename).st_size
 
 
-def getmtime(filename):
+def getmtime(filename, /):
     """Return the last modification time of a file, reported by os.stat()."""
     return os.stat(filename).st_mtime
 
 
-def getatime(filename):
+def getatime(filename, /):
     """Return the last access time of a file, reported by os.stat()."""
     return os.stat(filename).st_atime
 
 
-def getctime(filename):
+def getctime(filename, /):
     """Return the metadata change time of a file, reported by os.stat()."""
     return os.stat(filename).st_ctime
 
 
 # Return the longest prefix of all list elements.
-def commonprefix(m):
+def commonprefix(m, /):
     "Given a list of pathnames, returns the longest common leading component"
     if not m: return ''
     # Some people pass in a list of pathname parts to operate in an OS-agnostic
@@ -120,14 +120,14 @@ def commonprefix(m):
 
 # Are two stat buffers (obtained from stat, fstat or lstat)
 # describing the same file?
-def samestat(s1, s2):
+def samestat(s1, s2, /):
     """Test whether two stat buffers reference the same file"""
     return (s1.st_ino == s2.st_ino and
             s1.st_dev == s2.st_dev)
 
 
 # Are two filenames really pointing to the same file?
-def samefile(f1, f2):
+def samefile(f1, f2, /):
     """Test whether two pathnames reference the same actual file or directory
 
     This is determined by the device number and i-node number and
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index 9cdc16480f9afe..fad15430a373fb 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -47,7 +47,7 @@ def _get_bothseps(path):
         LOCALE_NAME_INVARIANT as _LOCALE_NAME_INVARIANT,
         LCMAP_LOWERCASE as _LCMAP_LOWERCASE)
 
-    def normcase(s):
+    def normcase(s, /):
         """Normalize case of pathname.
 
         Makes all characters lowercase and all slashes into backslashes.
@@ -66,7 +66,7 @@ def normcase(s):
                                   _LCMAP_LOWERCASE,
                                   s.replace('/', '\\'))
 except ImportError:
-    def normcase(s):
+    def normcase(s, /):
         """Normalize case of pathname.
 
         Makes all characters lowercase and all slashes into backslashes.
@@ -77,7 +77,7 @@ def normcase(s):
         return s.replace('/', '\\').lower()
 
 
-def isabs(s):
+def isabs(s, /):
     """Test whether a path is absolute"""
     s = os.fspath(s)
     if isinstance(s, bytes):
@@ -96,7 +96,7 @@ def isabs(s):
 
 
 # Join two (or more) paths.
-def join(path, *paths):
+def join(path, /, *paths):
     path = os.fspath(path)
     if isinstance(path, bytes):
         sep = b'\\'
@@ -143,7 +143,7 @@ def join(path, *paths):
 # Split a path in a drive specification (a drive letter followed by a
 # colon) and the path specification.
 # It is always true that drivespec + pathspec == p
-def splitdrive(p):
+def splitdrive(p, /):
     """Split a pathname into drive/UNC sharepoint and relative path specifiers.
     Returns a 2-tuple (drive_or_unc, path); either part may be empty.
 
@@ -169,7 +169,7 @@ def splitdrive(p):
 try:
     from nt import _path_splitroot_ex as splitroot
 except ImportError:
-    def splitroot(p):
+    def splitroot(p, /):
         """Split a pathname into drive, root and tail.
 
         The tail contains anything after the root."""
@@ -219,7 +219,7 @@ def splitroot(p):
 # join(head, tail) == p holds.
 # The resulting head won't end in '/' unless it is the root.
 
-def split(p):
+def split(p, /):
     """Split a pathname.
 
     Return tuple (head, tail) where tail is everything after the final slash.
@@ -240,7 +240,7 @@ def split(p):
 # pathname component; the root is everything before that.
 # It is always true that root + ext == p.
 
-def splitext(p):
+def splitext(p, /):
     p = os.fspath(p)
     if isinstance(p, bytes):
         return genericpath._splitext(p, b'\\', b'/', b'.')
@@ -251,14 +251,14 @@ def splitext(p):
 
 # Return the tail (basename) part of a path.
 
-def basename(p):
+def basename(p, /):
     """Returns the final component of a pathname"""
     return split(p)[1]
 
 
 # Return the head (dirname) part of a path.
 
-def dirname(p):
+def dirname(p, /):
     """Returns the directory component of a pathname"""
     return split(p)[0]
 
@@ -601,7 +601,7 @@ def abspath(path):
     from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink
 except ImportError:
     # realpath is a no-op on systems without _getfinalpathname support.
-    def realpath(path, *, strict=False):
+    def realpath(path, /, *, strict=False):
         return abspath(path)
 else:
     def _readlink_deep(path, ignored_error=OSError):
@@ -702,7 +702,7 @@ def _getfinalpathname_nonstrict(path, 
ignored_error=OSError):
                 tail = join(name, tail) if tail else name
         return tail
 
-    def realpath(path, *, strict=False):
+    def realpath(path, /, *, strict=False):
         path = normpath(path)
         if isinstance(path, bytes):
             prefix = b'\\\\?\\'
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index d38f3bd5872bcd..5b5cde239e6275 100644
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -50,7 +50,7 @@ def _get_sep(path):
 # normalizations (such as optimizing '../' away) are not allowed
 # (another function should be defined to do that).
 
-def normcase(s):
+def normcase(s, /):
     """Normalize case of pathname.  Has no effect under Posix"""
     return os.fspath(s)
 
@@ -58,7 +58,7 @@ def normcase(s):
 # Return whether a path is absolute.
 # Trivial in Posix, harder on the Mac or MS-DOS.
 
-def isabs(s):
+def isabs(s, /):
     """Test whether a path is absolute"""
     s = os.fspath(s)
     sep = _get_sep(s)
@@ -69,7 +69,7 @@ def isabs(s):
 # Ignore the previous parts if a part is absolute.
 # Insert a '/' unless the first part is empty or already ends in '/'.
 
-def join(a, *p):
+def join(a, /, *p):
     """Join two or more pathname components, inserting '/' as needed.
     If any component is an absolute path, all previous path components
     will be discarded.  An empty last part will result in a path that
@@ -97,7 +97,7 @@ def join(a, *p):
 # '/' in the path, head  will be empty.
 # Trailing '/'es are stripped from head unless it is the root.
 
-def split(p):
+def split(p, /):
     """Split a pathname.  Returns tuple "(head, tail)" where "tail" is
     everything after the final slash.  Either part may be empty."""
     p = os.fspath(p)
@@ -114,7 +114,7 @@ def split(p):
 # pathname component; the root is everything before that.
 # It is always true that root + ext == p.
 
-def splitext(p):
+def splitext(p, /):
     p = os.fspath(p)
     if isinstance(p, bytes):
         sep = b'/'
@@ -128,7 +128,7 @@ def splitext(p):
 # Split a pathname into a drive specification and the rest of the
 # path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.
 
-def splitdrive(p):
+def splitdrive(p, /):
     """Split a pathname into drive and path. On Posix, drive is always
     empty."""
     p = os.fspath(p)
@@ -138,7 +138,7 @@ def splitdrive(p):
 try:
     from posix import _path_splitroot_ex as splitroot
 except ImportError:
-    def splitroot(p):
+    def splitroot(p, /):
         """Split a pathname into drive, root and tail.
 
         The tail contains anything after the root."""
@@ -163,7 +163,7 @@ def splitroot(p):
 
 # Return the tail (basename) part of a path, same as split(path)[1].
 
-def basename(p):
+def basename(p, /):
     """Returns the final component of a pathname"""
     p = os.fspath(p)
     sep = _get_sep(p)
@@ -173,7 +173,7 @@ def basename(p):
 
 # Return the head (dirname) part of a path, same as split(path)[0].
 
-def dirname(p):
+def dirname(p, /):
     """Returns the directory component of a pathname"""
     p = os.fspath(p)
     sep = _get_sep(p)
@@ -388,7 +388,7 @@ def abspath(path):
 # Return a canonical path (i.e. the absolute location of a file on the
 # filesystem).
 
-def realpath(filename, *, strict=False):
+def realpath(filename, /, *, strict=False):
     """Return the canonical path of the specified filename, eliminating any
 symbolic links encountered in the path."""
     filename = os.fspath(filename)
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 0a281cbe6c57a2..8af9e1db781c8f 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -2044,57 +2044,24 @@ os__getvolumepathname(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs,
 #if defined(MS_WINDOWS)
 
 PyDoc_STRVAR(os__path_splitroot__doc__,
-"_path_splitroot($module, /, path)\n"
+"_path_splitroot($module, path, /)\n"
 "--\n"
 "\n"
 "Removes everything after the root on Win32.");
 
 #define OS__PATH_SPLITROOT_METHODDEF    \
-    {"_path_splitroot", _PyCFunction_CAST(os__path_splitroot), 
METH_FASTCALL|METH_KEYWORDS, os__path_splitroot__doc__},
+    {"_path_splitroot", (PyCFunction)os__path_splitroot, METH_O, 
os__path_splitroot__doc__},
 
 static PyObject *
 os__path_splitroot_impl(PyObject *module, path_t *path);
 
 static PyObject *
-os__path_splitroot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
+os__path_splitroot(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
-    #define NUM_KEYWORDS 1
-    static struct {
-        PyGC_Head _this_is_not_used;
-        PyObject_VAR_HEAD
-        Py_hash_t ob_hash;
-        PyObject *ob_item[NUM_KEYWORDS];
-    } _kwtuple = {
-        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
-        .ob_hash = -1,
-        .ob_item = { &_Py_ID(path), },
-    };
-    #undef NUM_KEYWORDS
-    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
-    #else  // !Py_BUILD_CORE
-    #  define KWTUPLE NULL
-    #endif  // !Py_BUILD_CORE
-
-    static const char * const _keywords[] = {"path", NULL};
-    static _PyArg_Parser _parser = {
-        .keywords = _keywords,
-        .fname = "_path_splitroot",
-        .kwtuple = KWTUPLE,
-    };
-    #undef KWTUPLE
-    PyObject *argsbuf[1];
     path_t path = PATH_T_INITIALIZE_P("_path_splitroot", "path", 0, 0, 0, 0);
 
-    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
-            /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
-    if (!args) {
-        goto exit;
-    }
-    if (!path_converter(args[0], &path)) {
+    if (!path_converter(arg, &path)) {
         goto exit;
     }
     return_value = os__path_splitroot_impl(module, &path);
@@ -2255,58 +2222,25 @@ os__path_lexists(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, PyOb
 #if defined(MS_WINDOWS)
 
 PyDoc_STRVAR(os__path_isdir__doc__,
-"_path_isdir($module, /, s)\n"
+"_path_isdir($module, path, /)\n"
 "--\n"
 "\n"
 "Return true if the pathname refers to an existing directory.");
 
 #define OS__PATH_ISDIR_METHODDEF    \
-    {"_path_isdir", _PyCFunction_CAST(os__path_isdir), 
METH_FASTCALL|METH_KEYWORDS, os__path_isdir__doc__},
+    {"_path_isdir", (PyCFunction)os__path_isdir, METH_O, 
os__path_isdir__doc__},
 
 static int
 os__path_isdir_impl(PyObject *module, path_t *path);
 
 static PyObject *
-os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
+os__path_isdir(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
-    #define NUM_KEYWORDS 1
-    static struct {
-        PyGC_Head _this_is_not_used;
-        PyObject_VAR_HEAD
-        Py_hash_t ob_hash;
-        PyObject *ob_item[NUM_KEYWORDS];
-    } _kwtuple = {
-        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
-        .ob_hash = -1,
-        .ob_item = { _Py_LATIN1_CHR('s'), },
-    };
-    #undef NUM_KEYWORDS
-    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
-    #else  // !Py_BUILD_CORE
-    #  define KWTUPLE NULL
-    #endif  // !Py_BUILD_CORE
-
-    static const char * const _keywords[] = {"s", NULL};
-    static _PyArg_Parser _parser = {
-        .keywords = _keywords,
-        .fname = "_path_isdir",
-        .kwtuple = KWTUPLE,
-    };
-    #undef KWTUPLE
-    PyObject *argsbuf[1];
     path_t path = PATH_T_INITIALIZE_P("_path_isdir", "path", 0, 0, 1, 1);
     int _return_value;
 
-    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
-            /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
-    if (!args) {
-        goto exit;
-    }
-    if (!path_converter(args[0], &path)) {
+    if (!path_converter(arg, &path)) {
         goto exit;
     }
     _return_value = os__path_isdir_impl(module, &path);
@@ -2541,7 +2475,7 @@ os__path_isjunction(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, P
 #endif /* defined(MS_WINDOWS) */
 
 PyDoc_STRVAR(os__path_splitroot_ex__doc__,
-"_path_splitroot_ex($module, /, p)\n"
+"_path_splitroot_ex($module, path, /)\n"
 "--\n"
 "\n"
 "Split a pathname into drive, root and tail.\n"
@@ -2549,51 +2483,18 @@ PyDoc_STRVAR(os__path_splitroot_ex__doc__,
 "The tail contains anything after the root.");
 
 #define OS__PATH_SPLITROOT_EX_METHODDEF    \
-    {"_path_splitroot_ex", _PyCFunction_CAST(os__path_splitroot_ex), 
METH_FASTCALL|METH_KEYWORDS, os__path_splitroot_ex__doc__},
+    {"_path_splitroot_ex", (PyCFunction)os__path_splitroot_ex, METH_O, 
os__path_splitroot_ex__doc__},
 
 static PyObject *
 os__path_splitroot_ex_impl(PyObject *module, path_t *path);
 
 static PyObject *
-os__path_splitroot_ex(PyObject *module, PyObject *const *args, Py_ssize_t 
nargs, PyObject *kwnames)
+os__path_splitroot_ex(PyObject *module, PyObject *arg)
 {
     PyObject *return_value = NULL;
-    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
-    #define NUM_KEYWORDS 1
-    static struct {
-        PyGC_Head _this_is_not_used;
-        PyObject_VAR_HEAD
-        Py_hash_t ob_hash;
-        PyObject *ob_item[NUM_KEYWORDS];
-    } _kwtuple = {
-        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
-        .ob_hash = -1,
-        .ob_item = { _Py_LATIN1_CHR('p'), },
-    };
-    #undef NUM_KEYWORDS
-    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
-    #else  // !Py_BUILD_CORE
-    #  define KWTUPLE NULL
-    #endif  // !Py_BUILD_CORE
-
-    static const char * const _keywords[] = {"p", NULL};
-    static _PyArg_Parser _parser = {
-        .keywords = _keywords,
-        .fname = "_path_splitroot_ex",
-        .kwtuple = KWTUPLE,
-    };
-    #undef KWTUPLE
-    PyObject *argsbuf[1];
     path_t path = PATH_T_INITIALIZE("_path_splitroot_ex", "path", 0, 1, 1, 0, 
0);
 
-    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
-            /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
-    if (!args) {
-        goto exit;
-    }
-    if (!path_converter(args[0], &path)) {
+    if (!path_converter(arg, &path)) {
         goto exit;
     }
     return_value = os__path_splitroot_ex_impl(module, &path);
@@ -13518,4 +13419,4 @@ os__emscripten_log(PyObject *module, PyObject *const 
*args, Py_ssize_t nargs, Py
 #ifndef OS__EMSCRIPTEN_LOG_METHODDEF
     #define OS__EMSCRIPTEN_LOG_METHODDEF
 #endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */
-/*[clinic end generated code: output=608e9bc5f631f688 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b1e2615384347102 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 77622fbc4e8065..b1a80788bd8115 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -5226,14 +5226,15 @@ os__getvolumepathname_impl(PyObject *module, path_t 
*path)
 /*[clinic input]
 os._path_splitroot
 
-    path: path_t
+    path: path_t,
+    /
 
 Removes everything after the root on Win32.
 [clinic start generated code]*/
 
 static PyObject *
 os__path_splitroot_impl(PyObject *module, path_t *path)
-/*[clinic end generated code: output=ab7f1a88b654581c input=dc93b1d3984cffb6]*/
+/*[clinic end generated code: output=ab7f1a88b654581c input=42831e41f8458f6d]*/
 {
     wchar_t *buffer;
     wchar_t *end;
@@ -5535,7 +5536,8 @@ os__path_lexists_impl(PyObject *module, path_t *path)
 /*[clinic input]
 os._path_isdir -> bool
 
-    s as path: path_t(allow_fd=True, suppress_value_error=True)
+    path: path_t(allow_fd=True, suppress_value_error=True),
+    /
 
 Return true if the pathname refers to an existing directory.
 
@@ -5543,7 +5545,7 @@ Return true if the pathname refers to an existing 
directory.
 
 static int
 os__path_isdir_impl(PyObject *module, path_t *path)
-/*[clinic end generated code: output=d5786196f9e2fa7a input=132a3b5301aecf79]*/
+/*[clinic end generated code: output=d5786196f9e2fa7a input=0d3fd790564d244b]*/
 {
     return _testFileType(path, PY_IFDIR);
 }
@@ -5612,7 +5614,8 @@ os__path_isjunction_impl(PyObject *module, path_t *path)
 /*[clinic input]
 os._path_splitroot_ex
 
-    p as path: path_t(make_wide=True, nonstrict=True)
+    path: path_t(make_wide=True, nonstrict=True),
+    /
 
 Split a pathname into drive, root and tail.
 
@@ -5621,7 +5624,7 @@ The tail contains anything after the root.
 
 static PyObject *
 os__path_splitroot_ex_impl(PyObject *module, path_t *path)
-/*[clinic end generated code: output=4b0072b6cdf4b611 input=4556b615c7cc13f2]*/
+/*[clinic end generated code: output=4b0072b6cdf4b611 input=4ac47b394d68bd21]*/
 {
     Py_ssize_t drvsize, rootsize;
     PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL;

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to