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