macpath.py 7.18 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","isdir","isfile",
9
           "walk","expanduser","expandvars","normpath","abspath",
10
           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
11
           "realpath","supports_unicode_filenames"]
12

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

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

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


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

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

37

38
def join(s, *p):
39 40 41 42 43 44 45 46 47
    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
48
        if path[-1:] != ':':
49 50 51 52
            path = path + ':'
        path = path + t
    return path

53 54

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

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

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

def splitext(p):
70 71 72 73 74
    """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."""

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

81

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


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

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

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

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

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

112

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

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

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

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


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

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

137

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

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

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

Guido van Rossum's avatar
Guido van Rossum committed
151
def exists(s):
152
    """Return True if the pathname refers to an existing file or directory."""
153 154 155 156

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

160 161 162 163 164 165 166 167
# 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 ''
    prefix = m[0]
    for item in m:
        for i in range(len(prefix)):
168
            if prefix[:i+1] != item[:i+1]:
169 170 171 172
                prefix = prefix[:i]
                if i == 0: return ''
                break
    return prefix
173

174
def expandvars(path):
175 176
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
177

178

179 180 181
def expanduser(path):
    """Dummy to retain interface-compatibility with other operating systems."""
    return path
182

183 184
class norm_error(Exception):
    """Path cannot be normalized"""
185 186

def normpath(s):
187 188 189 190 191 192
    """Normalize a pathname.  Will return the same result for
    equivalent paths."""

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

193
    comps = s.split(":")
194 195 196 197 198
    i = 1
    while i < len(comps)-1:
        if comps[i] == "" and comps[i-1] != "":
            if i > 1:
                del comps[i-1:i+1]
199
                i = i - 1
200 201
            else:
                # best way to handle this is to raise an exception
202
                raise norm_error, 'Cannot use :: immediately after volume name'
203
        else:
204 205
            i = i + 1

206
    s = ":".join(comps)
207 208 209 210

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

213 214

def walk(top, func, arg):
215 216 217 218 219 220 221 222 223 224 225 226 227
    """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."""
228 229 230 231 232 233 234 235

    try:
        names = os.listdir(top)
    except os.error:
        return
    func(arg, top, names)
    for name in names:
        name = join(top, name)
236
        if isdir(name) and not islink(name):
237 238 239
            walk(name, func, arg)


Guido van Rossum's avatar
Guido van Rossum committed
240
def abspath(path):
241
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
242 243 244
    if not isabs(path):
        path = join(os.getcwd(), path)
    return normpath(path)
245 246

# realpath is a no-op on systems without islink support
247
def realpath(path):
Tim Peters's avatar
Tim Peters committed
248 249
    path = abspath(path)
    try:
Jack Jansen's avatar
Jack Jansen committed
250
        import Carbon.File
Tim Peters's avatar
Tim Peters committed
251 252 253 254 255 256 257 258
    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
259
        path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname()
Tim Peters's avatar
Tim Peters committed
260
    return path
261 262

supports_unicode_filenames = False