cpython icon indicating copy to clipboard operation
cpython copied to clipboard

Same interface for `posixpath` & `ntpath`

Open nineteendo opened this issue 11 months ago • 0 comments

Feature or enhancement

Proposal:

Five changes:

  1. Deduplicate code in split, basename & dirname like in ntpath.
  2. Decouple logic for splitdrive from splitroot in ntpath.
  3. Deduplicate lexists implementations by defining it in genericpath.
  4. Add posixpath implementation for isreserved & isdevdrive to provide the same interface for posixpath & ntpath.
  5. Add isdevdrive to __all__ in ntpath.
 # ntpath.py
 __all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
            "basename","dirname","commonprefix","getsize","getmtime",
            "getatime","getctime", "islink","exists","lexists","isdir","isfile",
            "ismount","isreserved","expanduser","expandvars","normpath",
            "abspath","curdir","pardir","sep","pathsep","defpath","altsep",
            "extsep","devnull","realpath","supports_unicode_filenames","relpath",
-          "samefile", "sameopenfile", "samestat", "commonpath", "isjunction"]
+           "samefile", "sameopenfile", "samestat", "commonpath", "isjunction",
+           "isdevdrive"]
 
 def splitdrive(p):
     """Split a pathname into drive/UNC sharepoint and relative path specifiers.
     """
-    drive, root, tail = splitroot(p)
-    return drive, root + tail
+    p = os.fspath(p)
+    if isinstance(p, bytes):
+        sep = b'\\'
+        altsep = b'/'
+        colon = b':'
+        unc_prefix = b'\\\\?\\UNC\\'
+    else:
+        sep = '\\'
+        altsep = '/'
+        colon = ':'
+        unc_prefix = '\\\\?\\UNC\\'
+    normp = p.replace(altsep, sep)
+    if normp[:1] != sep:
+        if normp[1:2] == colon:
+            # Drive-letter drives, e.g. X:
+            return p[:2], p[2:]
+    elif normp[1:2] == sep:
+        # UNC drives, e.g. \\server\share or \\?\UNC\server\share
+        # Device drives, e.g. \\.\device or \\?\device
+        start = 8 if normp[:8].upper() == unc_prefix else 2
+        index = normp.find(sep, start)
+        if index == -1:
+            return p, p[:0]
+        index2 = normp.find(sep, index + 1)
+        if index2 == -1:
+            return p, p[:0]
+        return p[:index2], p[index2:]
+    return p[:0], p
 
 
 def splitroot(p):
     """Split a pathname into drive, root and tail."""
-    p = os.fspath(p)
+    drive, p = splitdrive(p)
     if isinstance(p, bytes):
         sep = b'\\'
         altsep = b'/'
-        colon = b':'
-        unc_prefix = b'\\\\?\\UNC\\'
-        empty = b''
     else:
         sep = '\\'
         altsep = '/'
-        colon = ':'
-        unc_prefix = '\\\\?\\UNC\\'
-        empty = ''
     normp = p.replace(altsep, sep)
     if normp[:1] == sep:
-        if normp[1:2] == sep:
-            # UNC drives, e.g. \\server\share or \\?\UNC\server\share
-            # Device drives, e.g. \\.\device or \\?\device
-            start = 8 if normp[:8].upper() == unc_prefix else 2
-            index = normp.find(sep, start)
-            if index == -1:
-                return p, empty, empty
-            index2 = normp.find(sep, index + 1)
-            if index2 == -1:
-                return p, empty, empty
-            return p[:index2], p[index2:index2 + 1], p[index2 + 1:]
-        else:
-            # Relative path with root, e.g. \Windows
-            return empty, p[:1], p[1:]
-    elif normp[1:2] == colon:
-        if normp[2:3] == sep:
-            # Absolute drive-letter path, e.g. X:\Windows
-            return p[:2], p[2:3], p[3:]
-        else:
-            # Relative path with drive, e.g. X:Windows
-            return p[:2], empty, p[2:]
-    else:
-        # Relative path, e.g. Windows
-        return empty, empty, p
+        # Absolute path, e.g. X:\Windows
+        return drive, p[:1], p[1:]
+    # Relative path, e.g. X:Windows
+    return drive, p[:0], p
  
-# Being true for dangling symbolic links is also useful.
-
-def lexists(path):
-    """Test whether a path exists.  Returns True for broken symbolic links"""
-    try:
-        st = os.lstat(path)
-    except (OSError, ValueError):
-        return False
-    return True
 # posixpath.py
 __all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
            "basename","dirname","commonprefix","getsize","getmtime",
            "getatime","getctime","islink","exists","lexists","isdir","isfile",
            "ismount", "expanduser","expandvars","normpath","abspath",
            "samefile","sameopenfile","samestat",
            "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
            "devnull","realpath","supports_unicode_filenames","relpath",
-           "commonpath", "isjunction"]
+           "commonpath", "isjunction","isreserved","isdevdrive"]
 
 def basename(p):
     """Returns the final component of a pathname"""
-    p = os.fspath(p)
-    sep = _get_sep(p)
-    i = p.rfind(sep) + 1
-    return p[I:]
+    return split(p)[1]
 
 def dirname(p):
     """Returns the directory component of a pathname"""
-    p = os.fspath(p)
-    sep = _get_sep(p)
-    i = p.rfind(sep) + 1
-    head = p[:I]
-    if head and head != sep*len(head):
-        head = head.rstrip(sep)
-    return head
+    return split(p)[0]
 
-# Being true for dangling symbolic links is also useful.
-
-def lexists(path):
-    """Test whether a path exists.  Returns True for broken symbolic links"""
-    try:
-        os.lstat(path)
-    except (OSError, ValueError):
-        return False
-    return True
 
+def isreserved(path):
+    """Return true if the pathname is reserved by the system.
+    Always returns False on posix"""
+    return False
+
+def isdevdrive(path):
+    """Determines whether the specified path is on a Dev Drive.
+    Dev Drives are not a part of posix semantics"""
+    return False
 # genericpath.py
 __all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
            'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
-           'samestat']
+           'samestat', 'lexists']
 
+# Being true for dangling symbolic links is also useful.
+def lexists(path):
+    """Test whether a path exists.  Returns True for broken symbolic links"""
+    try:
+        os.lstat(path)
+    except (OSError, ValueError):
+        return False

Has this already been discussed elsewhere?

This is a minor feature, which does not need previous discussion elsewhere

Links to previous discussion of this feature:

No response

Linked PRs

  • gh-117115

nineteendo avatar Mar 21 '24 09:03 nineteendo