posixpath.py 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
"""Common operations on Posix pathnames.

Instead of importing this module directly, import os and refer to
this module as os.path.  The "os.path" name is an alias for this
module on Posix systems; on other systems (e.g. Mac, Windows),
os.path provides the same operations in a manner specific to that
platform, and is an alias to another module (e.g. macpath, ntpath).

Some of this can actually be useful on non-Posix systems too, e.g.
for manipulation of the pathname component of URLs.
11
"""
12 13

import os
14
import stat
Guido van Rossum's avatar
Guido van Rossum committed
15

16 17 18 19 20
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
           "basename","dirname","commonprefix","getsize","getmtime",
           "getatime","islink","exists","isdir","isfile","ismount",
           "walk","expanduser","expandvars","normpath","abspath",
           "samefile","sameopenfile","samestat"]
Guido van Rossum's avatar
Guido van Rossum committed
21

Guido van Rossum's avatar
Guido van Rossum committed
22 23 24 25 26 27
# Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
# On MS-DOS this may also turn slashes into backslashes; however, other
# normalizations (such as optimizing '../' away) are not allowed
# (another function should be defined to do that).

def normcase(s):
28 29
    """Normalize case of pathname.  Has no effect under Posix"""
    return s
Guido van Rossum's avatar
Guido van Rossum committed
30 31


Jeremy Hylton's avatar
Jeremy Hylton committed
32
# Return whether a path is absolute.
Guido van Rossum's avatar
Guido van Rossum committed
33 34 35
# Trivial in Posix, harder on the Mac or MS-DOS.

def isabs(s):
36 37
    """Test whether a path is absolute"""
    return s[:1] == '/'
Guido van Rossum's avatar
Guido van Rossum committed
38 39


40 41
# Join pathnames.
# Ignore the previous parts if a part is absolute.
Guido van Rossum's avatar
Guido van Rossum committed
42
# Insert a '/' unless the first part is empty or already ends in '/'.
Guido van Rossum's avatar
Guido van Rossum committed
43

44
def join(a, *p):
45 46 47 48 49 50 51 52 53 54
    """Join two or more pathname components, inserting '/' as needed"""
    path = a
    for b in p:
        if b[:1] == '/':
            path = b
        elif path == '' or path[-1:] == '/':
            path = path + b
        else:
            path = path + '/' + b
    return path
Guido van Rossum's avatar
Guido van Rossum committed
55 56


57
# Split a path in head (everything up to the last '/') and tail (the
58 59 60
# rest).  If the path ends in '/', tail will be empty.  If there is no
# '/' in the path, head  will be empty.
# Trailing '/'es are stripped from head unless it is the root.
Guido van Rossum's avatar
Guido van Rossum committed
61

Guido van Rossum's avatar
Guido van Rossum committed
62
def split(p):
Tim Peters's avatar
Tim Peters committed
63
    """Split a pathname.  Returns tuple "(head, tail)" where "tail" is
Fred Drake's avatar
Fred Drake committed
64
    everything after the final slash.  Either part may be empty."""
65
    i = p.rfind('/') + 1
66
    head, tail = p[:i], p[i:]
67
    if head and head != '/'*len(head):
68 69 70
        while head[-1] == '/':
            head = head[:-1]
    return head, tail
Guido van Rossum's avatar
Guido van Rossum committed
71 72


Guido van Rossum's avatar
Guido van Rossum committed
73
# Split a path in root and extension.
74
# The extension is everything starting at the last dot in the last
Guido van Rossum's avatar
Guido van Rossum committed
75
# pathname component; the root is everything before that.
Guido van Rossum's avatar
Guido van Rossum committed
76 77
# It is always true that root + ext == p.

Guido van Rossum's avatar
Guido van Rossum committed
78
def splitext(p):
79
    """Split the extension from a pathname.  Extension is everything from the
Fred Drake's avatar
Fred Drake committed
80
    last dot to the end.  Returns "(root, ext)", either part may be empty."""
81 82 83 84 85 86 87 88 89 90 91 92 93 94
    root, ext = '', ''
    for c in p:
        if c == '/':
            root, ext = root + ext + c, ''
        elif c == '.':
            if ext:
                root, ext = root + ext, c
            else:
                ext = c
        elif ext:
            ext = ext + c
        else:
            root = root + c
    return root, ext
Guido van Rossum's avatar
Guido van Rossum committed
95 96


Guido van Rossum's avatar
Guido van Rossum committed
97 98 99 100
# Split a pathname into a drive specification and the rest of the
# path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.

def splitdrive(p):
Tim Peters's avatar
Tim Peters committed
101
    """Split a pathname into drive and path. On Posix, drive is always
Fred Drake's avatar
Fred Drake committed
102
    empty."""
103
    return '', p
Guido van Rossum's avatar
Guido van Rossum committed
104 105


Guido van Rossum's avatar
Guido van Rossum committed
106
# Return the tail (basename) part of a path.
Guido van Rossum's avatar
Guido van Rossum committed
107

Guido van Rossum's avatar
Guido van Rossum committed
108
def basename(p):
109 110
    """Returns the final component of a pathname"""
    return split(p)[1]
Guido van Rossum's avatar
Guido van Rossum committed
111 112


113 114 115
# Return the head (dirname) part of a path.

def dirname(p):
116 117
    """Returns the directory component of a pathname"""
    return split(p)[0]
118 119


Guido van Rossum's avatar
Guido van Rossum committed
120
# Return the longest prefix of all list elements.
Guido van Rossum's avatar
Guido van Rossum committed
121

Guido van Rossum's avatar
Guido van Rossum committed
122
def commonprefix(m):
123 124
    "Given a list of pathnames, returns the longest common leading component"
    if not m: return ''
125 126
    prefix = m[0]
    for item in m:
127
        for i in range(len(prefix)):
128
            if prefix[:i+1] != item[:i+1]:
129 130 131
                prefix = prefix[:i]
                if i == 0: return ''
                break
132
    return prefix
Guido van Rossum's avatar
Guido van Rossum committed
133 134


135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
# Get size, mtime, atime of files.

def getsize(filename):
    """Return the size of a file, reported by os.stat()."""
    st = os.stat(filename)
    return st[stat.ST_SIZE]

def getmtime(filename):
    """Return the last modification time of a file, reported by os.stat()."""
    st = os.stat(filename)
    return st[stat.ST_MTIME]

def getatime(filename):
    """Return the last access time of a file, reported by os.stat()."""
    st = os.stat(filename)
150
    return st[stat.ST_ATIME]
151 152


Guido van Rossum's avatar
Guido van Rossum committed
153
# Is a path a symbolic link?
154
# This will always return false on systems where os.lstat doesn't exist.
Guido van Rossum's avatar
Guido van Rossum committed
155 156

def islink(path):
157 158 159 160 161 162
    """Test whether a path is a symbolic link"""
    try:
        st = os.lstat(path)
    except (os.error, AttributeError):
        return 0
    return stat.S_ISLNK(st[stat.ST_MODE])
Guido van Rossum's avatar
Guido van Rossum committed
163 164 165 166 167


# Does a path exist?
# This is false for dangling symbolic links.

Guido van Rossum's avatar
Guido van Rossum committed
168
def exists(path):
169 170 171 172 173 174
    """Test whether a path exists.  Returns false for broken symbolic links"""
    try:
        st = os.stat(path)
    except os.error:
        return 0
    return 1
Guido van Rossum's avatar
Guido van Rossum committed
175 176


177
# Is a path a directory?
Guido van Rossum's avatar
Guido van Rossum committed
178 179 180
# This follows symbolic links, so both islink() and isdir() can be true
# for the same path.

Guido van Rossum's avatar
Guido van Rossum committed
181
def isdir(path):
182 183 184 185 186 187
    """Test whether a path is a directory"""
    try:
        st = os.stat(path)
    except os.error:
        return 0
    return stat.S_ISDIR(st[stat.ST_MODE])
Guido van Rossum's avatar
Guido van Rossum committed
188 189


190
# Is a path a regular file?
191
# This follows symbolic links, so both islink() and isfile() can be true
Guido van Rossum's avatar
Guido van Rossum committed
192 193 194
# for the same path.

def isfile(path):
195 196 197 198 199 200
    """Test whether a path is a regular file"""
    try:
        st = os.stat(path)
    except os.error:
        return 0
    return stat.S_ISREG(st[stat.ST_MODE])
Guido van Rossum's avatar
Guido van Rossum committed
201 202


203
# Are two filenames really pointing to the same file?
Guido van Rossum's avatar
Guido van Rossum committed
204

205
def samefile(f1, f2):
206 207 208 209
    """Test whether two pathnames reference the same actual file"""
    s1 = os.stat(f1)
    s2 = os.stat(f2)
    return samestat(s1, s2)
210 211 212 213


# Are two open files really referencing the same file?
# (Not necessarily the same file descriptor!)
Guido van Rossum's avatar
Guido van Rossum committed
214

215
def sameopenfile(fp1, fp2):
216 217 218 219
    """Test whether two open file objects reference the same file"""
    s1 = os.fstat(fp1)
    s2 = os.fstat(fp2)
    return samestat(s1, s2)
220 221 222 223


# Are two stat buffers (obtained from stat, fstat or lstat)
# describing the same file?
Guido van Rossum's avatar
Guido van Rossum committed
224

225
def samestat(s1, s2):
226 227
    """Test whether two stat buffers reference the same file"""
    return s1[stat.ST_INO] == s2[stat.ST_INO] and \
Tim Peters's avatar
Tim Peters committed
228
           s1[stat.ST_DEV] == s2[stat.ST_DEV]
Guido van Rossum's avatar
Guido van Rossum committed
229 230 231


# Is a path a mount point?
232
# (Does this work for all UNIXes?  Is it even guaranteed to work by Posix?)
Guido van Rossum's avatar
Guido van Rossum committed
233

Guido van Rossum's avatar
Guido van Rossum committed
234
def ismount(path):
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    """Test whether a path is a mount point"""
    try:
        s1 = os.stat(path)
        s2 = os.stat(join(path, '..'))
    except os.error:
        return 0 # It doesn't exist -- so not a mount point :-)
    dev1 = s1[stat.ST_DEV]
    dev2 = s2[stat.ST_DEV]
    if dev1 != dev2:
        return 1        # path/.. on a different device as path
    ino1 = s1[stat.ST_INO]
    ino2 = s2[stat.ST_INO]
    if ino1 == ino2:
        return 1        # path/.. is the same i-node as path
    return 0
Guido van Rossum's avatar
Guido van Rossum committed
250 251 252


# Directory tree walk.
Guido van Rossum's avatar
Guido van Rossum committed
253 254 255
# For each directory under top (including top itself, but excluding
# '.' and '..'), func(arg, dirname, filenames) is called, where
# dirname is the name of the directory and filenames is the list
256
# of files (and subdirectories etc.) in the directory.
Guido van Rossum's avatar
Guido van Rossum committed
257
# The func may modify the filenames list, to implement a filter,
Guido van Rossum's avatar
Guido van Rossum committed
258
# or to impose a different order of visiting.
Guido van Rossum's avatar
Guido van Rossum committed
259

Guido van Rossum's avatar
Guido van Rossum committed
260
def walk(top, func, arg):
Tim Peters's avatar
Tim Peters committed
261
    """walk(top,func,arg) calls func(arg, d, files) for each directory "d"
Fred Drake's avatar
Fred Drake committed
262 263 264
    in the tree  rooted at "top" (including "top" itself).  "files" is a list
    of all the files and subdirs in directory "d".
    """
265 266 267 268 269 270
    try:
        names = os.listdir(top)
    except os.error:
        return
    func(arg, top, names)
    for name in names:
Tim Peters's avatar
Tim Peters committed
271
        name = join(top, name)
272 273 274 275
        try:
            st = os.lstat(name)
        except os.error:
            continue
Tim Peters's avatar
Tim Peters committed
276 277
        if stat.S_ISDIR(st[stat.ST_MODE]):
            walk(name, func, arg)
Guido van Rossum's avatar
Guido van Rossum committed
278 279 280 281 282 283 284 285 286 287 288 289


# Expand paths beginning with '~' or '~user'.
# '~' means $HOME; '~user' means that user's home directory.
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
# the path is returned unchanged (leaving error reporting to whatever
# function is called with the expanded path as argument).
# See also module 'glob' for expansion of *, ? and [...] in pathnames.
# (A function should also be defined to do full *sh-style environment
# variable expansion.)

def expanduser(path):
Tim Peters's avatar
Tim Peters committed
290
    """Expand ~ and ~user constructions.  If user or $HOME is unknown,
Fred Drake's avatar
Fred Drake committed
291
    do nothing."""
292
    if path[:1] != '~':
293 294
        return path
    i, n = 1, len(path)
295
    while i < n and path[i] != '/':
Fred Drake's avatar
Fred Drake committed
296
        i = i + 1
297 298 299 300 301 302 303 304 305 306 307
    if i == 1:
        if not os.environ.has_key('HOME'):
            return path
        userhome = os.environ['HOME']
    else:
        import pwd
        try:
            pwent = pwd.getpwnam(path[1:i])
        except KeyError:
            return path
        userhome = pwent[5]
Fred Drake's avatar
Fred Drake committed
308
    if userhome[-1:] == '/': i = i + 1
309
    return userhome + path[i:]
310 311 312


# Expand paths containing shell variable substitutions.
313
# This expands the forms $variable and ${variable} only.
Jeremy Hylton's avatar
Jeremy Hylton committed
314
# Non-existent variables are left unchanged.
315 316

_varprog = None
317 318

def expandvars(path):
319
    """Expand shell variables of form $var and ${var}.  Unknown variables
Fred Drake's avatar
Fred Drake committed
320
    are left unchanged."""
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
    global _varprog
    if '$' not in path:
        return path
    if not _varprog:
        import re
        _varprog = re.compile(r'\$(\w+|\{[^}]*\})')
    i = 0
    while 1:
        m = _varprog.search(path, i)
        if not m:
            break
        i, j = m.span(0)
        name = m.group(1)
        if name[:1] == '{' and name[-1:] == '}':
            name = name[1:-1]
        if os.environ.has_key(name):
            tail = path[j:]
            path = path[:i] + os.environ[name]
            i = len(path)
            path = path + tail
        else:
            i = j
    return path
344 345 346 347 348 349 350


# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
# It should be understood that this may change the meaning of the path
# if it contains symbolic links!

def normpath(path):
351
    """Normalize path, eliminating double slashes, etc."""
352 353
    if path == '':
        return '.'
354 355 356
    initial_slashes = path.startswith('/')
    # POSIX allows one or two initial slashes, but treats three or more
    # as single slash.
Tim Peters's avatar
Tim Peters committed
357
    if (initial_slashes and
358 359
        path.startswith('//') and not path.startswith('///')):
        initial_slashes = 2
360
    comps = path.split('/')
361 362 363 364
    new_comps = []
    for comp in comps:
        if comp in ('', '.'):
            continue
365
        if (comp != '..' or (not initial_slashes and not new_comps) or
366 367 368 369 370
             (new_comps and new_comps[-1] == '..')):
            new_comps.append(comp)
        elif new_comps:
            new_comps.pop()
    comps = new_comps
371
    path = '/'.join(comps)
372 373
    if initial_slashes:
        path = '/'*initial_slashes + path
374
    return path or '.'
Guido van Rossum's avatar
Guido van Rossum committed
375 376 377


def abspath(path):
378
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
379 380 381
    if not isabs(path):
        path = join(os.getcwd(), path)
    return normpath(path)