1""" 2Path operations common to more than one OS 3Do not use directly. The OS specific modules import the appropriate 4functions from this module themselves. 5""" 6import os 7import stat 8 9__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', 10 'getsize', 'isdevdrive', 'isdir', 'isfile', 'isjunction', 'islink', 11 'lexists', 'samefile', 'sameopenfile', 'samestat'] 12 13 14# Does a path exist? 15# This is false for dangling symbolic links on systems that support them. 16def exists(path): 17 """Test whether a path exists. Returns False for broken symbolic links""" 18 try: 19 os.stat(path) 20 except (OSError, ValueError): 21 return False 22 return True 23 24 25# Being true for dangling symbolic links is also useful. 26def lexists(path): 27 """Test whether a path exists. Returns True for broken symbolic links""" 28 try: 29 os.lstat(path) 30 except (OSError, ValueError): 31 return False 32 return True 33 34# This follows symbolic links, so both islink() and isdir() can be true 35# for the same path on systems that support symlinks 36def isfile(path): 37 """Test whether a path is a regular file""" 38 try: 39 st = os.stat(path) 40 except (OSError, ValueError): 41 return False 42 return stat.S_ISREG(st.st_mode) 43 44 45# Is a path a directory? 46# This follows symbolic links, so both islink() and isdir() 47# can be true for the same path on systems that support symlinks 48def isdir(s): 49 """Return true if the pathname refers to an existing directory.""" 50 try: 51 st = os.stat(s) 52 except (OSError, ValueError): 53 return False 54 return stat.S_ISDIR(st.st_mode) 55 56 57# Is a path a symbolic link? 58# This will always return false on systems where os.lstat doesn't exist. 59 60def islink(path): 61 """Test whether a path is a symbolic link""" 62 try: 63 st = os.lstat(path) 64 except (OSError, ValueError, AttributeError): 65 return False 66 return stat.S_ISLNK(st.st_mode) 67 68 69# Is a path a junction? 70def isjunction(path): 71 """Test whether a path is a junction 72 Junctions are not supported on the current platform""" 73 os.fspath(path) 74 return False 75 76 77def isdevdrive(path): 78 """Determines whether the specified path is on a Windows Dev Drive. 79 Dev Drives are not supported on the current platform""" 80 os.fspath(path) 81 return False 82 83 84def getsize(filename): 85 """Return the size of a file, reported by os.stat().""" 86 return os.stat(filename).st_size 87 88 89def getmtime(filename): 90 """Return the last modification time of a file, reported by os.stat().""" 91 return os.stat(filename).st_mtime 92 93 94def getatime(filename): 95 """Return the last access time of a file, reported by os.stat().""" 96 return os.stat(filename).st_atime 97 98 99def getctime(filename): 100 """Return the metadata change time of a file, reported by os.stat().""" 101 return os.stat(filename).st_ctime 102 103 104# Return the longest prefix of all list elements. 105def commonprefix(m): 106 "Given a list of pathnames, returns the longest common leading component" 107 if not m: return '' 108 # Some people pass in a list of pathname parts to operate in an OS-agnostic 109 # fashion; don't try to translate in that case as that's an abuse of the 110 # API and they are already doing what they need to be OS-agnostic and so 111 # they most likely won't be using an os.PathLike object in the sublists. 112 if not isinstance(m[0], (list, tuple)): 113 m = tuple(map(os.fspath, m)) 114 s1 = min(m) 115 s2 = max(m) 116 for i, c in enumerate(s1): 117 if c != s2[i]: 118 return s1[:i] 119 return s1 120 121# Are two stat buffers (obtained from stat, fstat or lstat) 122# describing the same file? 123def samestat(s1, s2): 124 """Test whether two stat buffers reference the same file""" 125 return (s1.st_ino == s2.st_ino and 126 s1.st_dev == s2.st_dev) 127 128 129# Are two filenames really pointing to the same file? 130def samefile(f1, f2): 131 """Test whether two pathnames reference the same actual file or directory 132 133 This is determined by the device number and i-node number and 134 raises an exception if an os.stat() call on either pathname fails. 135 """ 136 s1 = os.stat(f1) 137 s2 = os.stat(f2) 138 return samestat(s1, s2) 139 140 141# Are two open files really referencing the same file? 142# (Not necessarily the same file descriptor!) 143def sameopenfile(fp1, fp2): 144 """Test whether two open file objects reference the same file""" 145 s1 = os.fstat(fp1) 146 s2 = os.fstat(fp2) 147 return samestat(s1, s2) 148 149 150# Split a path in root and extension. 151# The extension is everything starting at the last dot in the last 152# pathname component; the root is everything before that. 153# It is always true that root + ext == p. 154 155# Generic implementation of splitext, to be parametrized with 156# the separators 157def _splitext(p, sep, altsep, extsep): 158 """Split the extension from a pathname. 159 160 Extension is everything from the last dot to the end, ignoring 161 leading dots. Returns "(root, ext)"; ext may be empty.""" 162 # NOTE: This code must work for text and bytes strings. 163 164 sepIndex = p.rfind(sep) 165 if altsep: 166 altsepIndex = p.rfind(altsep) 167 sepIndex = max(sepIndex, altsepIndex) 168 169 dotIndex = p.rfind(extsep) 170 if dotIndex > sepIndex: 171 # skip all leading dots 172 filenameIndex = sepIndex + 1 173 while filenameIndex < dotIndex: 174 if p[filenameIndex:filenameIndex+1] != extsep: 175 return p[:dotIndex], p[dotIndex:] 176 filenameIndex += 1 177 178 return p, p[:0] 179 180def _check_arg_types(funcname, *args): 181 hasstr = hasbytes = False 182 for s in args: 183 if isinstance(s, str): 184 hasstr = True 185 elif isinstance(s, bytes): 186 hasbytes = True 187 else: 188 raise TypeError(f'{funcname}() argument must be str, bytes, or ' 189 f'os.PathLike object, not {s.__class__.__name__!r}') from None 190 if hasstr and hasbytes: 191 raise TypeError("Can't mix strings and bytes in path components") from None 192