macpath.py 5.77 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 54 55
    try:
        colon = _get_colon(s)
        path = s
56 57
        if not p:
            path[:0] + colon  #23780: Ensure compatible data type even if p is null.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
        for t in p:
            if (not path) or isabs(t):
                path = t
                continue
            if t[:1] == colon:
                t = t[1:]
            if colon not in path:
                path = colon + path
            if path[-1:] != colon:
                path = path + colon
            path = path + t
        return path
    except (TypeError, AttributeError, BytesWarning):
        genericpath._check_arg_types('join', s, *p)
        raise
73

74 75

def split(s):
76 77 78
    """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."""
79

80 81 82
    colon = _get_colon(s)
    if colon not in s: return s[:0], s
    col = 0
83
    for i in range(len(s)):
84 85 86 87
        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
88
    return path, file
89

Guido van Rossum's avatar
Guido van Rossum committed
90 91

def splitext(p):
92 93 94 95
    if isinstance(p, bytes):
        return genericpath._splitext(p, b':', altsep, b'.')
    else:
        return genericpath._splitext(p, sep, altsep, extsep)
96
splitext.__doc__ = genericpath._splitext.__doc__
97

98
def splitdrive(p):
99 100 101 102 103 104
    """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)."""

105
    return p[:0], p
106 107


108
# Short interfaces to split()
Guido van Rossum's avatar
Guido van Rossum committed
109

110 111
def dirname(s): return split(s)[0]
def basename(s): return split(s)[1]
Guido van Rossum's avatar
Guido van Rossum committed
112

Jack Jansen's avatar
Jack Jansen committed
113
def ismount(s):
Tim Peters's avatar
Tim Peters committed
114 115 116
    if not isabs(s):
        return False
    components = split(s)
117
    return len(components) == 2 and not components[1]
118

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

122
    try:
Jack Jansen's avatar
Jack Jansen committed
123 124
        import Carbon.File
        return Carbon.File.ResolveAliasFile(s, 0)[2]
125 126
    except:
        return False
Guido van Rossum's avatar
Guido van Rossum committed
127

128 129 130 131 132 133 134 135
# 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)
136
    except OSError:
137 138 139
        return False
    return True

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

144

145 146 147
def expanduser(path):
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
148

149 150
class norm_error(Exception):
    """Path cannot be normalized"""
151 152

def normpath(s):
153 154 155
    """Normalize a pathname.  Will return the same result for
    equivalent paths."""

156 157 158 159
    colon = _get_colon(s)

    if colon not in s:
        return colon + s
160

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

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 180
    return s

Guido van Rossum's avatar
Guido van Rossum committed
181
def abspath(path):
182
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
183
    if not isabs(path):
184 185 186 187 188
        if isinstance(path, bytes):
            cwd = os.getcwdb()
        else:
            cwd = os.getcwd()
        path = join(cwd, path)
Guido van Rossum's avatar
Guido van Rossum committed
189
    return normpath(path)
190 191

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

211
supports_unicode_filenames = True