macpath.py 5.49 KB
Newer Older
1
"""Pathname and path-related operations for the Macintosh."""
Guido van Rossum's avatar
Guido van Rossum committed
2

3
import os
Guido van Rossum's avatar
Guido van Rossum committed
4
from stat import *
5
import genericpath
6
from genericpath import *
Guido van Rossum's avatar
Guido van Rossum committed
7

8 9
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
           "basename","dirname","commonprefix","getsize","getmtime",
10
           "getatime","getctime", "islink","exists","lexists","isdir","isfile",
Benjamin Peterson's avatar
Benjamin Peterson committed
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 18 19 20 21 22 23
curdir = ':'
pardir = '::'
extsep = '.'
sep = ':'
pathsep = '\n'
defpath = ':'
altsep = None
24
devnull = 'Dev:Null'
25

26 27 28 29 30 31
def _get_colon(path):
    if isinstance(path, bytes):
        return b':'
    else:
        return ':'

32
# Normalize the case of a pathname.  Dummy in Posix, but <s>.lower() here.
33

34
def normcase(path):
35 36 37
    if not isinstance(path, (bytes, str)):
        raise TypeError("normcase() argument must be str or bytes, "
                        "not '{}'".format(path.__class__.__name__))
38
    return path.lower()
39 40


Guido van Rossum's avatar
Guido van Rossum committed
41
def isabs(s):
42 43 44 45 46 47
    """Return true if a path is absolute.
    On the Mac, relative paths begin with a colon,
    but as a special case, paths with no colons at all are also relative.
    Anything else is absolute (the string up to the first colon is the
    volume name)."""

48 49
    colon = _get_colon(s)
    return colon in s and s[:1] != colon
Guido van Rossum's avatar
Guido van Rossum committed
50

51

52
def join(s, *p):
53
    colon = _get_colon(s)
54 55
    path = s
    for t in p:
56
        if (not path) or isabs(t):
57 58
            path = t
            continue
59
        if t[:1] == colon:
60
            t = t[1:]
61 62 63 64
        if colon not in path:
            path = colon + path
        if path[-1:] != colon:
            path = path + colon
65 66 67
        path = path + t
    return path

68 69

def split(s):
70 71 72
    """Split a pathname into two parts: the directory leading up to the final
    bit, and the basename (the filename, without colons, in that directory).
    The result (s, t) is such that join(s, t) yields the original argument."""
73

74 75 76
    colon = _get_colon(s)
    if colon not in s: return s[:0], s
    col = 0
77
    for i in range(len(s)):
78 79 80 81
        if s[i:i+1] == colon: col = i + 1
    path, file = s[:col-1], s[col:]
    if path and not colon in path:
        path = path + colon
82
    return path, file
83

Guido van Rossum's avatar
Guido van Rossum committed
84 85

def splitext(p):
86 87 88 89
    if isinstance(p, bytes):
        return genericpath._splitext(p, b':', altsep, b'.')
    else:
        return genericpath._splitext(p, sep, altsep, extsep)
90
splitext.__doc__ = genericpath._splitext.__doc__
91

92
def splitdrive(p):
93 94 95 96 97 98
    """Split a pathname into a drive specification and the rest of the
    path.  Useful on DOS/Windows/NT; on the Mac, the drive is always
    empty (don't use the volume name -- it doesn't have the same
    syntactic and semantic oddities as DOS drive letters, such as there
    being a separate current directory per drive)."""

99
    return p[:0], p
100 101


102
# Short interfaces to split()
Guido van Rossum's avatar
Guido van Rossum committed
103

104 105
def dirname(s): return split(s)[0]
def basename(s): return split(s)[1]
Guido van Rossum's avatar
Guido van Rossum committed
106

Jack Jansen's avatar
Jack Jansen committed
107
def ismount(s):
Tim Peters's avatar
Tim Peters committed
108 109 110
    if not isabs(s):
        return False
    components = split(s)
111
    return len(components) == 2 and not components[1]
112

Guido van Rossum's avatar
Guido van Rossum committed
113
def islink(s):
114
    """Return true if the pathname refers to a symbolic link."""
Guido van Rossum's avatar
Guido van Rossum committed
115

116
    try:
Jack Jansen's avatar
Jack Jansen committed
117 118
        import Carbon.File
        return Carbon.File.ResolveAliasFile(s, 0)[2]
119 120
    except:
        return False
Guido van Rossum's avatar
Guido van Rossum committed
121

122 123 124 125 126 127 128 129
# Is `stat`/`lstat` a meaningful difference on the Mac?  This is safe in any
# case.

def lexists(path):
    """Test whether a path exists.  Returns True for broken symbolic links"""

    try:
        st = os.lstat(path)
130
    except OSError:
131 132 133
        return False
    return True

134
def expandvars(path):
135 136
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
137

138

139 140 141
def expanduser(path):
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
142

143 144
class norm_error(Exception):
    """Path cannot be normalized"""
145 146

def normpath(s):
147 148 149
    """Normalize a pathname.  Will return the same result for
    equivalent paths."""

150 151 152 153
    colon = _get_colon(s)

    if colon not in s:
        return colon + s
154

155
    comps = s.split(colon)
156 157
    i = 1
    while i < len(comps)-1:
158
        if not comps[i] and comps[i-1]:
159 160
            if i > 1:
                del comps[i-1:i+1]
161
                i = i - 1
162 163
            else:
                # best way to handle this is to raise an exception
164
                raise norm_error('Cannot use :: immediately after volume name')
165
        else:
166 167
            i = i + 1

168
    s = colon.join(comps)
169 170

    # remove trailing ":" except for ":" and "Volume:"
171
    if s[-1:] == colon and len(comps) > 2 and s != colon*len(s):
172
        s = s[:-1]
173 174
    return s

Guido van Rossum's avatar
Guido van Rossum committed
175
def abspath(path):
176
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
177
    if not isabs(path):
178 179 180 181 182
        if isinstance(path, bytes):
            cwd = os.getcwdb()
        else:
            cwd = os.getcwd()
        path = join(cwd, path)
Guido van Rossum's avatar
Guido van Rossum committed
183
    return normpath(path)
184 185

# realpath is a no-op on systems without islink support
186
def realpath(path):
Tim Peters's avatar
Tim Peters committed
187 188
    path = abspath(path)
    try:
Jack Jansen's avatar
Jack Jansen committed
189
        import Carbon.File
190
    except ImportError:
Tim Peters's avatar
Tim Peters committed
191 192 193
        return path
    if not path:
        return path
194 195 196
    colon = _get_colon(path)
    components = path.split(colon)
    path = components[0] + colon
Tim Peters's avatar
Tim Peters committed
197 198
    for c in components[1:]:
        path = join(path, c)
199 200 201 202
        try:
            path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname()
        except Carbon.File.Error:
            pass
Tim Peters's avatar
Tim Peters committed
203
    return path
204

205
supports_unicode_filenames = True