1"""Pathname and path-related operations for the Macintosh.""" 2 3# strings representing various path-related bits and pieces 4# These are primarily for export; internally, they are hardcoded. 5# Should be set before imports for resolving cyclic dependency. 6curdir = ':' 7pardir = '::' 8extsep = '.' 9sep = ':' 10pathsep = '\n' 11defpath = ':' 12altsep = None 13devnull = 'Dev:Null' 14 15import os 16from stat import * 17import genericpath 18from genericpath import * 19import warnings 20 21warnings.warn('the macpath module is deprecated in 3.7 and will be removed ' 22 'in 3.8', DeprecationWarning, stacklevel=2) 23 24__all__ = ["normcase","isabs","join","splitdrive","split","splitext", 25 "basename","dirname","commonprefix","getsize","getmtime", 26 "getatime","getctime", "islink","exists","lexists","isdir","isfile", 27 "expanduser","expandvars","normpath","abspath", 28 "curdir","pardir","sep","pathsep","defpath","altsep","extsep", 29 "devnull","realpath","supports_unicode_filenames"] 30 31def _get_colon(path): 32 if isinstance(path, bytes): 33 return b':' 34 else: 35 return ':' 36 37# Normalize the case of a pathname. Dummy in Posix, but <s>.lower() here. 38 39def normcase(path): 40 if not isinstance(path, (bytes, str)): 41 raise TypeError("normcase() argument must be str or bytes, " 42 "not '{}'".format(path.__class__.__name__)) 43 return path.lower() 44 45 46def isabs(s): 47 """Return true if a path is absolute. 48 On the Mac, relative paths begin with a colon, 49 but as a special case, paths with no colons at all are also relative. 50 Anything else is absolute (the string up to the first colon is the 51 volume name).""" 52 53 colon = _get_colon(s) 54 return colon in s and s[:1] != colon 55 56 57def join(s, *p): 58 try: 59 colon = _get_colon(s) 60 path = s 61 if not p: 62 path[:0] + colon #23780: Ensure compatible data type even if p is null. 63 for t in p: 64 if (not path) or isabs(t): 65 path = t 66 continue 67 if t[:1] == colon: 68 t = t[1:] 69 if colon not in path: 70 path = colon + path 71 if path[-1:] != colon: 72 path = path + colon 73 path = path + t 74 return path 75 except (TypeError, AttributeError, BytesWarning): 76 genericpath._check_arg_types('join', s, *p) 77 raise 78 79 80def split(s): 81 """Split a pathname into two parts: the directory leading up to the final 82 bit, and the basename (the filename, without colons, in that directory). 83 The result (s, t) is such that join(s, t) yields the original argument.""" 84 85 colon = _get_colon(s) 86 if colon not in s: return s[:0], s 87 col = 0 88 for i in range(len(s)): 89 if s[i:i+1] == colon: col = i + 1 90 path, file = s[:col-1], s[col:] 91 if path and not colon in path: 92 path = path + colon 93 return path, file 94 95 96def splitext(p): 97 if isinstance(p, bytes): 98 return genericpath._splitext(p, b':', altsep, b'.') 99 else: 100 return genericpath._splitext(p, sep, altsep, extsep) 101splitext.__doc__ = genericpath._splitext.__doc__ 102 103def splitdrive(p): 104 """Split a pathname into a drive specification and the rest of the 105 path. Useful on DOS/Windows/NT; on the Mac, the drive is always 106 empty (don't use the volume name -- it doesn't have the same 107 syntactic and semantic oddities as DOS drive letters, such as there 108 being a separate current directory per drive).""" 109 110 return p[:0], p 111 112 113# Short interfaces to split() 114 115def dirname(s): return split(s)[0] 116def basename(s): return split(s)[1] 117 118def ismount(s): 119 if not isabs(s): 120 return False 121 components = split(s) 122 return len(components) == 2 and not components[1] 123 124def islink(s): 125 """Return true if the pathname refers to a symbolic link.""" 126 127 try: 128 import Carbon.File 129 return Carbon.File.ResolveAliasFile(s, 0)[2] 130 except: 131 return False 132 133# Is `stat`/`lstat` a meaningful difference on the Mac? This is safe in any 134# case. 135 136def lexists(path): 137 """Test whether a path exists. Returns True for broken symbolic links""" 138 139 try: 140 st = os.lstat(path) 141 except OSError: 142 return False 143 return True 144 145def expandvars(path): 146 """Dummy to retain interface-compatibility with other operating systems.""" 147 return path 148 149 150def expanduser(path): 151 """Dummy to retain interface-compatibility with other operating systems.""" 152 return path 153 154class norm_error(Exception): 155 """Path cannot be normalized""" 156 157def normpath(s): 158 """Normalize a pathname. Will return the same result for 159 equivalent paths.""" 160 161 colon = _get_colon(s) 162 163 if colon not in s: 164 return colon + s 165 166 comps = s.split(colon) 167 i = 1 168 while i < len(comps)-1: 169 if not comps[i] and comps[i-1]: 170 if i > 1: 171 del comps[i-1:i+1] 172 i = i - 1 173 else: 174 # best way to handle this is to raise an exception 175 raise norm_error('Cannot use :: immediately after volume name') 176 else: 177 i = i + 1 178 179 s = colon.join(comps) 180 181 # remove trailing ":" except for ":" and "Volume:" 182 if s[-1:] == colon and len(comps) > 2 and s != colon*len(s): 183 s = s[:-1] 184 return s 185 186def abspath(path): 187 """Return an absolute path.""" 188 if not isabs(path): 189 if isinstance(path, bytes): 190 cwd = os.getcwdb() 191 else: 192 cwd = os.getcwd() 193 path = join(cwd, path) 194 return normpath(path) 195 196# realpath is a no-op on systems without islink support 197def realpath(path): 198 path = abspath(path) 199 try: 200 import Carbon.File 201 except ImportError: 202 return path 203 if not path: 204 return path 205 colon = _get_colon(path) 206 components = path.split(colon) 207 path = components[0] + colon 208 for c in components[1:]: 209 path = join(path, c) 210 try: 211 path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname() 212 except Carbon.File.Error: 213 pass 214 return path 215 216supports_unicode_filenames = True 217