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