posixpath.py 10.3 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


Guido van Rossum's avatar
Guido van Rossum committed
17 18 19 20 21 22
# 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):
23 24
    """Normalize case of pathname.  Has no effect under Posix"""
    return s
Guido van Rossum's avatar
Guido van Rossum committed
25 26


Jeremy Hylton's avatar
Jeremy Hylton committed
27
# Return whether a path is absolute.
Guido van Rossum's avatar
Guido van Rossum committed
28 29 30
# Trivial in Posix, harder on the Mac or MS-DOS.

def isabs(s):
31 32
    """Test whether a path is absolute"""
    return s[:1] == '/'
Guido van Rossum's avatar
Guido van Rossum committed
33 34


35 36
# Join pathnames.
# Ignore the previous parts if a part is absolute.
Guido van Rossum's avatar
Guido van Rossum committed
37
# Insert a '/' unless the first part is empty or already ends in '/'.
Guido van Rossum's avatar
Guido van Rossum committed
38

39
def join(a, *p):
40 41 42 43 44 45 46 47 48 49
    """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
50 51


52
# Split a path in head (everything up to the last '/') and tail (the
53 54 55
# 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
56

Guido van Rossum's avatar
Guido van Rossum committed
57
def split(p):
58 59 60 61 62 63 64 65 66
    """Split a pathname.  Returns tuple "(head, tail)" where "tail" is 
everything after the final slash.  Either part may be empty"""
    import string
    i = string.rfind(p, '/') + 1
    head, tail = p[:i], p[i:]
    if head and head <> '/'*len(head):
        while head[-1] == '/':
            head = head[:-1]
    return head, tail
Guido van Rossum's avatar
Guido van Rossum committed
67 68


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

Guido van Rossum's avatar
Guido van Rossum committed
74
def splitext(p):
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    """Split the extension from a pathname.  Extension is everything from the
last dot to the end.  Returns "(root, ext)", either part may be empty"""
    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
91 92


Guido van Rossum's avatar
Guido van Rossum committed
93 94 95 96
# 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):
97 98 99
    """Split a pathname into drive and path. On Posix, drive is always 
empty"""
    return '', p
Guido van Rossum's avatar
Guido van Rossum committed
100 101


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

Guido van Rossum's avatar
Guido van Rossum committed
104
def basename(p):
105 106
    """Returns the final component of a pathname"""
    return split(p)[1]
Guido van Rossum's avatar
Guido van Rossum committed
107 108


109 110 111
# Return the head (dirname) part of a path.

def dirname(p):
112 113
    """Returns the directory component of a pathname"""
    return split(p)[0]
114 115


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

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


131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
# 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)
146
    return st[stat.ST_ATIME]
147 148


Guido van Rossum's avatar
Guido van Rossum committed
149
# Is a path a symbolic link?
150
# This will always return false on systems where os.lstat doesn't exist.
Guido van Rossum's avatar
Guido van Rossum committed
151 152

def islink(path):
153 154 155 156 157 158
    """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
159 160 161 162 163


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

Guido van Rossum's avatar
Guido van Rossum committed
164
def exists(path):
165 166 167 168 169 170
    """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
171 172


173
# Is a path a directory?
Guido van Rossum's avatar
Guido van Rossum committed
174 175 176
# 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
177
def isdir(path):
178 179 180 181 182 183
    """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
184 185


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

def isfile(path):
191 192 193 194 195 196
    """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
197 198


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

201
def samefile(f1, f2):
202 203 204 205
    """Test whether two pathnames reference the same actual file"""
    s1 = os.stat(f1)
    s2 = os.stat(f2)
    return samestat(s1, s2)
206 207 208 209


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

211
def sameopenfile(fp1, fp2):
212 213 214 215
    """Test whether two open file objects reference the same file"""
    s1 = os.fstat(fp1)
    s2 = os.fstat(fp2)
    return samestat(s1, s2)
216 217 218 219


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

221
def samestat(s1, s2):
222 223 224
    """Test whether two stat buffers reference the same file"""
    return s1[stat.ST_INO] == s2[stat.ST_INO] and \
	   s1[stat.ST_DEV] == s2[stat.ST_DEV]
Guido van Rossum's avatar
Guido van Rossum committed
225 226 227


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

Guido van Rossum's avatar
Guido van Rossum committed
230
def ismount(path):
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    """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
246 247 248


# Directory tree walk.
Guido van Rossum's avatar
Guido van Rossum committed
249 250 251
# 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
252
# of files (and subdirectories etc.) in the directory.
Guido van Rossum's avatar
Guido van Rossum committed
253
# The func may modify the filenames list, to implement a filter,
Guido van Rossum's avatar
Guido van Rossum committed
254
# or to impose a different order of visiting.
Guido van Rossum's avatar
Guido van Rossum committed
255

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


# 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):
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
    """Expand ~ and ~user constructions.  If user or $HOME is unknown, 
do nothing"""
    if path[:1] <> '~':
        return path
    i, n = 1, len(path)
    while i < n and path[i] <> '/':
        i = i+1
    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]
    if userhome[-1:] == '/': i = i+1
    return userhome + path[i:]
303 304 305


# Expand paths containing shell variable substitutions.
306
# This expands the forms $variable and ${variable} only.
Jeremy Hylton's avatar
Jeremy Hylton committed
307
# Non-existent variables are left unchanged.
308 309

_varprog = None
310 311

def expandvars(path):
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
    """Expand shell variables of form $var and ${var}.  Unknown variables
are left unchanged"""
    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
337 338 339 340 341 342 343


# 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):
344
    """Normalize path, eliminating double slashes, etc."""
345 346
    if path == '':
        return '.'
347
    import string
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    initial_slash = (path[0] == '/')
    comps = string.split(path, '/')
    new_comps = []
    for comp in comps:
        if comp in ('', '.'):
            continue
        if (comp != '..' or (not initial_slash and not new_comps) or 
             (new_comps and new_comps[-1] == '..')):
            new_comps.append(comp)
        elif new_comps:
            new_comps.pop()
    comps = new_comps
    path = string.join(comps, '/')
    if initial_slash:
        path = '/' + path
    return path or '.'
Guido van Rossum's avatar
Guido van Rossum committed
364 365 366


def abspath(path):
367
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
368 369 370
    if not isabs(path):
        path = join(os.getcwd(), path)
    return normpath(path)