macpath.py 7.42 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 5
from stat import *

6 7
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
           "basename","dirname","commonprefix","getsize","getmtime",
8
           "getatime","getctime", "islink","exists","lexists","isdir","isfile",
9
           "walk","expanduser","expandvars","normpath","abspath",
10
           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
11
           "devnull","realpath","supports_unicode_filenames"]
12

13 14 15 16 17 18 19 20
# strings representing various path-related bits and pieces
curdir = ':'
pardir = '::'
extsep = '.'
sep = ':'
pathsep = '\n'
defpath = ':'
altsep = None
21
devnull = 'Dev:Null'
22

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

25 26
def normcase(path):
    return path.lower()
27 28


Guido van Rossum's avatar
Guido van Rossum committed
29
def isabs(s):
30 31 32 33 34 35
    """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)."""

36
    return ':' in s and s[0] != ':'
Guido van Rossum's avatar
Guido van Rossum committed
37

38

39
def join(s, *p):
40 41 42 43 44 45 46 47 48
    path = s
    for t in p:
        if (not s) or isabs(t):
            path = t
            continue
        if t[:1] == ':':
            t = t[1:]
        if ':' not in path:
            path = ':' + path
49
        if path[-1:] != ':':
50 51 52 53
            path = path + ':'
        path = path + t
    return path

54 55

def split(s):
56 57 58
    """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."""
59

60 61 62
    if ':' not in s: return '', s
    colon = 0
    for i in range(len(s)):
63
        if s[i] == ':': colon = i + 1
64 65 66 67
    path, file = s[:colon-1], s[colon:]
    if path and not ':' in path:
        path = path + ':'
    return path, file
68

Guido van Rossum's avatar
Guido van Rossum committed
69 70

def splitext(p):
71 72 73 74 75
    """Split a path into root and extension.
    The extension is everything starting at the last dot in the last
    pathname component; the root is everything before that.
    It is always true that root + ext == p."""

76 77 78 79 80
    i = p.rfind('.')
    if i<=p.rfind(':'):
        return p, ''
    else:
        return p[:i], p[i:]
81

82

83
def splitdrive(p):
84 85 86 87 88 89 90
    """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)."""

    return '', p
91 92


93
# Short interfaces to split()
Guido van Rossum's avatar
Guido van Rossum committed
94

95 96
def dirname(s): return split(s)[0]
def basename(s): return split(s)[1]
Guido van Rossum's avatar
Guido van Rossum committed
97

Jack Jansen's avatar
Jack Jansen committed
98
def ismount(s):
Tim Peters's avatar
Tim Peters committed
99 100 101 102
    if not isabs(s):
        return False
    components = split(s)
    return len(components) == 2 and components[1] == ''
103

Guido van Rossum's avatar
Guido van Rossum committed
104
def isdir(s):
105 106 107 108 109 110
    """Return true if the pathname refers to an existing directory."""

    try:
        st = os.stat(s)
    except os.error:
        return 0
111
    return S_ISDIR(st.st_mode)
Guido van Rossum's avatar
Guido van Rossum committed
112

113

114 115 116 117
# Get size, mtime, atime of files.

def getsize(filename):
    """Return the size of a file, reported by os.stat()."""
118
    return os.stat(filename).st_size
119 120 121

def getmtime(filename):
    """Return the last modification time of a file, reported by os.stat()."""
122
    return os.stat(filename).st_mtime
123 124 125

def getatime(filename):
    """Return the last access time of a file, reported by os.stat()."""
126
    return os.stat(filename).st_atime
127 128


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

132
    try:
Jack Jansen's avatar
Jack Jansen committed
133 134
        import Carbon.File
        return Carbon.File.ResolveAliasFile(s, 0)[2]
135 136
    except:
        return False
Guido van Rossum's avatar
Guido van Rossum committed
137

138

Guido van Rossum's avatar
Guido van Rossum committed
139
def isfile(s):
140
    """Return true if the pathname refers to an existing regular file."""
Guido van Rossum's avatar
Guido van Rossum committed
141

142 143 144
    try:
        st = os.stat(s)
    except os.error:
145
        return False
146
    return S_ISREG(st.st_mode)
147

148 149 150
def getctime(filename):
    """Return the creation time of a file, reported by os.stat()."""
    return os.stat(filename).st_ctime
151

Guido van Rossum's avatar
Guido van Rossum committed
152
def exists(s):
153
    """Test whether a path exists.  Returns False for broken symbolic links"""
154 155 156 157

    try:
        st = os.stat(s)
    except os.error:
158 159
        return False
    return True
160

161 162 163 164 165 166 167 168 169 170 171 172
# 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)
    except os.error:
        return False
    return True

173 174 175 176 177
# Return the longest prefix of all list elements.

def commonprefix(m):
    "Given a list of pathnames, returns the longest common leading component"
    if not m: return ''
178 179 180 181 182 183 184 185
    s1 = min(m)
    s2 = max(m)
    n = min(len(s1), len(s2))
    for i in xrange(n):
        if s1[i] != s2[i]:
            return s1[:i]
    return s1[:n]

186

187
def expandvars(path):
188 189
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
190

191

192 193 194
def expanduser(path):
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
195

196 197
class norm_error(Exception):
    """Path cannot be normalized"""
198 199

def normpath(s):
200 201 202 203 204 205
    """Normalize a pathname.  Will return the same result for
    equivalent paths."""

    if ":" not in s:
        return ":"+s

206
    comps = s.split(":")
207 208 209 210 211
    i = 1
    while i < len(comps)-1:
        if comps[i] == "" and comps[i-1] != "":
            if i > 1:
                del comps[i-1:i+1]
212
                i = i - 1
213 214
            else:
                # best way to handle this is to raise an exception
215
                raise norm_error, 'Cannot use :: immediately after volume name'
216
        else:
217 218
            i = i + 1

219
    s = ":".join(comps)
220 221 222 223

    # remove trailing ":" except for ":" and "Volume:"
    if s[-1] == ":" and len(comps) > 2 and s != ":"*len(s):
        s = s[:-1]
224 225
    return s

226 227

def walk(top, func, arg):
228 229 230 231 232 233 234 235 236 237 238 239 240
    """Directory tree walk with callback function.

    For each directory in the directory tree rooted at top (including top
    itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
    dirname is the name of the directory, and fnames a list of the names of
    the files and subdirectories in dirname (excluding '.' and '..').  func
    may modify the fnames list in-place (e.g. via del or slice assignment),
    and walk will only recurse into the subdirectories whose names remain in
    fnames; this can be used to implement a filter, or to impose a specific
    order of visiting.  No semantics are defined for, or required of, arg,
    beyond that arg is always passed to func.  It can be used, e.g., to pass
    a filename pattern, or a mutable object designed to accumulate
    statistics.  Passing None for arg is common."""
241 242 243 244 245 246 247 248

    try:
        names = os.listdir(top)
    except os.error:
        return
    func(arg, top, names)
    for name in names:
        name = join(top, name)
249
        if isdir(name) and not islink(name):
250 251 252
            walk(name, func, arg)


Guido van Rossum's avatar
Guido van Rossum committed
253
def abspath(path):
254
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
255 256 257
    if not isabs(path):
        path = join(os.getcwd(), path)
    return normpath(path)
258 259

# realpath is a no-op on systems without islink support
260
def realpath(path):
Tim Peters's avatar
Tim Peters committed
261 262
    path = abspath(path)
    try:
Jack Jansen's avatar
Jack Jansen committed
263
        import Carbon.File
Tim Peters's avatar
Tim Peters committed
264 265 266 267 268 269 270 271
    except ImportError:
        return path
    if not path:
        return path
    components = path.split(':')
    path = components[0] + ':'
    for c in components[1:]:
        path = join(path, c)
Jack Jansen's avatar
Jack Jansen committed
272
        path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname()
Tim Peters's avatar
Tim Peters committed
273
    return path
274 275

supports_unicode_filenames = False