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