~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Lib/posixpath.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Common operations on Posix pathnames.
 
2
 
 
3
Instead of importing this module directly, import os and refer to
 
4
this module as os.path.  The "os.path" name is an alias for this
 
5
module on Posix systems; on other systems (e.g. Mac, Windows),
 
6
os.path provides the same operations in a manner specific to that
 
7
platform, and is an alias to another module (e.g. macpath, ntpath).
 
8
 
 
9
Some of this can actually be useful on non-Posix systems too, e.g.
 
10
for manipulation of the pathname component of URLs.
 
11
"""
 
12
 
 
13
import os
 
14
import sys
 
15
import stat
 
16
import genericpath
 
17
from genericpath import *
 
18
 
 
19
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
 
20
           "basename","dirname","commonprefix","getsize","getmtime",
 
21
           "getatime","getctime","islink","exists","lexists","isdir","isfile",
 
22
           "ismount", "expanduser","expandvars","normpath","abspath",
 
23
           "samefile","sameopenfile","samestat",
 
24
           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
 
25
           "devnull","realpath","supports_unicode_filenames","relpath"]
 
26
 
 
27
# Strings representing various path-related bits and pieces.
 
28
# These are primarily for export; internally, they are hardcoded.
 
29
curdir = '.'
 
30
pardir = '..'
 
31
extsep = '.'
 
32
sep = '/'
 
33
pathsep = ':'
 
34
defpath = ':/bin:/usr/bin'
 
35
altsep = None
 
36
devnull = '/dev/null'
 
37
 
 
38
def _get_sep(path):
 
39
    if isinstance(path, bytes):
 
40
        return b'/'
 
41
    else:
 
42
        return '/'
 
43
 
 
44
# Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
 
45
# On MS-DOS this may also turn slashes into backslashes; however, other
 
46
# normalizations (such as optimizing '../' away) are not allowed
 
47
# (another function should be defined to do that).
 
48
 
 
49
def normcase(s):
 
50
    """Normalize case of pathname.  Has no effect under Posix"""
 
51
    # TODO: on Mac OS X, this should really return s.lower().
 
52
    return s
 
53
 
 
54
 
 
55
# Return whether a path is absolute.
 
56
# Trivial in Posix, harder on the Mac or MS-DOS.
 
57
 
 
58
def isabs(s):
 
59
    """Test whether a path is absolute"""
 
60
    sep = _get_sep(s)
 
61
    return s.startswith(sep)
 
62
 
 
63
 
 
64
# Join pathnames.
 
65
# Ignore the previous parts if a part is absolute.
 
66
# Insert a '/' unless the first part is empty or already ends in '/'.
 
67
 
 
68
def join(a, *p):
 
69
    """Join two or more pathname components, inserting '/' as needed.
 
70
    If any component is an absolute path, all previous path components
 
71
    will be discarded."""
 
72
    sep = _get_sep(a)
 
73
    path = a
 
74
    for b in p:
 
75
        if b.startswith(sep):
 
76
            path = b
 
77
        elif not path or path.endswith(sep):
 
78
            path +=  b
 
79
        else:
 
80
            path += sep + b
 
81
    return path
 
82
 
 
83
 
 
84
# Split a path in head (everything up to the last '/') and tail (the
 
85
# rest).  If the path ends in '/', tail will be empty.  If there is no
 
86
# '/' in the path, head  will be empty.
 
87
# Trailing '/'es are stripped from head unless it is the root.
 
88
 
 
89
def split(p):
 
90
    """Split a pathname.  Returns tuple "(head, tail)" where "tail" is
 
91
    everything after the final slash.  Either part may be empty."""
 
92
    sep = _get_sep(p)
 
93
    i = p.rfind(sep) + 1
 
94
    head, tail = p[:i], p[i:]
 
95
    if head and head != sep*len(head):
 
96
        head = head.rstrip(sep)
 
97
    return head, tail
 
98
 
 
99
 
 
100
# Split a path in root and extension.
 
101
# The extension is everything starting at the last dot in the last
 
102
# pathname component; the root is everything before that.
 
103
# It is always true that root + ext == p.
 
104
 
 
105
def splitext(p):
 
106
    if isinstance(p, bytes):
 
107
        sep = b'/'
 
108
        extsep = b'.'
 
109
    else:
 
110
        sep = '/'
 
111
        extsep = '.'
 
112
    return genericpath._splitext(p, sep, None, extsep)
 
113
splitext.__doc__ = genericpath._splitext.__doc__
 
114
 
 
115
# Split a pathname into a drive specification and the rest of the
 
116
# path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.
 
117
 
 
118
def splitdrive(p):
 
119
    """Split a pathname into drive and path. On Posix, drive is always
 
120
    empty."""
 
121
    return p[:0], p
 
122
 
 
123
 
 
124
# Return the tail (basename) part of a path, same as split(path)[1].
 
125
 
 
126
def basename(p):
 
127
    """Returns the final component of a pathname"""
 
128
    sep = _get_sep(p)
 
129
    i = p.rfind(sep) + 1
 
130
    return p[i:]
 
131
 
 
132
 
 
133
# Return the head (dirname) part of a path, same as split(path)[0].
 
134
 
 
135
def dirname(p):
 
136
    """Returns the directory component of a pathname"""
 
137
    sep = _get_sep(p)
 
138
    i = p.rfind(sep) + 1
 
139
    head = p[:i]
 
140
    if head and head != sep*len(head):
 
141
        head = head.rstrip(sep)
 
142
    return head
 
143
 
 
144
 
 
145
# Is a path a symbolic link?
 
146
# This will always return false on systems where os.lstat doesn't exist.
 
147
 
 
148
def islink(path):
 
149
    """Test whether a path is a symbolic link"""
 
150
    try:
 
151
        st = os.lstat(path)
 
152
    except (os.error, AttributeError):
 
153
        return False
 
154
    return stat.S_ISLNK(st.st_mode)
 
155
 
 
156
# Being true for dangling symbolic links is also useful.
 
157
 
 
158
def lexists(path):
 
159
    """Test whether a path exists.  Returns True for broken symbolic links"""
 
160
    try:
 
161
        st = os.lstat(path)
 
162
    except os.error:
 
163
        return False
 
164
    return True
 
165
 
 
166
 
 
167
# Are two filenames really pointing to the same file?
 
168
 
 
169
def samefile(f1, f2):
 
170
    """Test whether two pathnames reference the same actual file"""
 
171
    s1 = os.stat(f1)
 
172
    s2 = os.stat(f2)
 
173
    return samestat(s1, s2)
 
174
 
 
175
 
 
176
# Are two open files really referencing the same file?
 
177
# (Not necessarily the same file descriptor!)
 
178
 
 
179
def sameopenfile(fp1, fp2):
 
180
    """Test whether two open file objects reference the same file"""
 
181
    s1 = os.fstat(fp1)
 
182
    s2 = os.fstat(fp2)
 
183
    return samestat(s1, s2)
 
184
 
 
185
 
 
186
# Are two stat buffers (obtained from stat, fstat or lstat)
 
187
# describing the same file?
 
188
 
 
189
def samestat(s1, s2):
 
190
    """Test whether two stat buffers reference the same file"""
 
191
    return s1.st_ino == s2.st_ino and \
 
192
           s1.st_dev == s2.st_dev
 
193
 
 
194
 
 
195
# Is a path a mount point?
 
196
# (Does this work for all UNIXes?  Is it even guaranteed to work by Posix?)
 
197
 
 
198
def ismount(path):
 
199
    """Test whether a path is a mount point"""
 
200
    try:
 
201
        s1 = os.lstat(path)
 
202
        if isinstance(path, bytes):
 
203
            parent = join(path, b'..')
 
204
        else:
 
205
            parent = join(path, '..')
 
206
        s2 = os.lstat(parent)
 
207
    except os.error:
 
208
        return False # It doesn't exist -- so not a mount point :-)
 
209
    dev1 = s1.st_dev
 
210
    dev2 = s2.st_dev
 
211
    if dev1 != dev2:
 
212
        return True     # path/.. on a different device as path
 
213
    ino1 = s1.st_ino
 
214
    ino2 = s2.st_ino
 
215
    if ino1 == ino2:
 
216
        return True     # path/.. is the same i-node as path
 
217
    return False
 
218
 
 
219
 
 
220
# Expand paths beginning with '~' or '~user'.
 
221
# '~' means $HOME; '~user' means that user's home directory.
 
222
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
 
223
# the path is returned unchanged (leaving error reporting to whatever
 
224
# function is called with the expanded path as argument).
 
225
# See also module 'glob' for expansion of *, ? and [...] in pathnames.
 
226
# (A function should also be defined to do full *sh-style environment
 
227
# variable expansion.)
 
228
 
 
229
def expanduser(path):
 
230
    """Expand ~ and ~user constructions.  If user or $HOME is unknown,
 
231
    do nothing."""
 
232
    if isinstance(path, bytes):
 
233
        tilde = b'~'
 
234
    else:
 
235
        tilde = '~'
 
236
    if not path.startswith(tilde):
 
237
        return path
 
238
    sep = _get_sep(path)
 
239
    i = path.find(sep, 1)
 
240
    if i < 0:
 
241
        i = len(path)
 
242
    if i == 1:
 
243
        if 'HOME' not in os.environ:
 
244
            import pwd
 
245
            userhome = pwd.getpwuid(os.getuid()).pw_dir
 
246
        else:
 
247
            userhome = os.environ['HOME']
 
248
    else:
 
249
        import pwd
 
250
        name = path[1:i]
 
251
        if isinstance(name, bytes):
 
252
            name = str(name, 'ASCII')
 
253
        try:
 
254
            pwent = pwd.getpwnam(name)
 
255
        except KeyError:
 
256
            return path
 
257
        userhome = pwent.pw_dir
 
258
    if isinstance(path, bytes):
 
259
        userhome = userhome.encode(sys.getfilesystemencoding())
 
260
    userhome = userhome.rstrip(sep)
 
261
    return userhome + path[i:]
 
262
 
 
263
 
 
264
# Expand paths containing shell variable substitutions.
 
265
# This expands the forms $variable and ${variable} only.
 
266
# Non-existent variables are left unchanged.
 
267
 
 
268
_varprog = None
 
269
_varprogb = None
 
270
 
 
271
def expandvars(path):
 
272
    """Expand shell variables of form $var and ${var}.  Unknown variables
 
273
    are left unchanged."""
 
274
    global _varprog, _varprogb
 
275
    if isinstance(path, bytes):
 
276
        if b'$' not in path:
 
277
            return path
 
278
        if not _varprogb:
 
279
            import re
 
280
            _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
 
281
        search = _varprogb.search
 
282
        start = b'{'
 
283
        end = b'}'
 
284
    else:
 
285
        if '$' not in path:
 
286
            return path
 
287
        if not _varprog:
 
288
            import re
 
289
            _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
 
290
        search = _varprog.search
 
291
        start = '{'
 
292
        end = '}'
 
293
    i = 0
 
294
    while True:
 
295
        m = search(path, i)
 
296
        if not m:
 
297
            break
 
298
        i, j = m.span(0)
 
299
        name = m.group(1)
 
300
        if name.startswith(start) and name.endswith(end):
 
301
            name = name[1:-1]
 
302
        if isinstance(name, bytes):
 
303
            name = str(name, 'ASCII')
 
304
        if name in os.environ:
 
305
            tail = path[j:]
 
306
            value = os.environ[name]
 
307
            if isinstance(path, bytes):
 
308
                value = value.encode('ASCII')
 
309
            path = path[:i] + value
 
310
            i = len(path)
 
311
            path += tail
 
312
        else:
 
313
            i = j
 
314
    return path
 
315
 
 
316
 
 
317
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
 
318
# It should be understood that this may change the meaning of the path
 
319
# if it contains symbolic links!
 
320
 
 
321
def normpath(path):
 
322
    """Normalize path, eliminating double slashes, etc."""
 
323
    if isinstance(path, bytes):
 
324
        sep = b'/'
 
325
        empty = b''
 
326
        dot = b'.'
 
327
        dotdot = b'..'
 
328
    else:
 
329
        sep = '/'
 
330
        empty = ''
 
331
        dot = '.'
 
332
        dotdot = '..'
 
333
    if path == empty:
 
334
        return dot
 
335
    initial_slashes = path.startswith(sep)
 
336
    # POSIX allows one or two initial slashes, but treats three or more
 
337
    # as single slash.
 
338
    if (initial_slashes and
 
339
        path.startswith(sep*2) and not path.startswith(sep*3)):
 
340
        initial_slashes = 2
 
341
    comps = path.split(sep)
 
342
    new_comps = []
 
343
    for comp in comps:
 
344
        if comp in (empty, dot):
 
345
            continue
 
346
        if (comp != dotdot or (not initial_slashes and not new_comps) or
 
347
             (new_comps and new_comps[-1] == dotdot)):
 
348
            new_comps.append(comp)
 
349
        elif new_comps:
 
350
            new_comps.pop()
 
351
    comps = new_comps
 
352
    path = sep.join(comps)
 
353
    if initial_slashes:
 
354
        path = sep*initial_slashes + path
 
355
    return path or dot
 
356
 
 
357
 
 
358
def abspath(path):
 
359
    """Return an absolute path."""
 
360
    if not isabs(path):
 
361
        if isinstance(path, bytes):
 
362
            cwd = os.getcwdb()
 
363
        else:
 
364
            cwd = os.getcwd()
 
365
        path = join(cwd, path)
 
366
    return normpath(path)
 
367
 
 
368
 
 
369
# Return a canonical path (i.e. the absolute location of a file on the
 
370
# filesystem).
 
371
 
 
372
def realpath(filename):
 
373
    """Return the canonical path of the specified filename, eliminating any
 
374
symbolic links encountered in the path."""
 
375
    if isinstance(filename, bytes):
 
376
        sep = b'/'
 
377
        empty = b''
 
378
    else:
 
379
        sep = '/'
 
380
        empty = ''
 
381
    if isabs(filename):
 
382
        bits = [sep] + filename.split(sep)[1:]
 
383
    else:
 
384
        bits = [empty] + filename.split(sep)
 
385
 
 
386
    for i in range(2, len(bits)+1):
 
387
        component = join(*bits[0:i])
 
388
        # Resolve symbolic links.
 
389
        if islink(component):
 
390
            resolved = _resolve_link(component)
 
391
            if resolved is None:
 
392
                # Infinite loop -- return original component + rest of the path
 
393
                return abspath(join(*([component] + bits[i:])))
 
394
            else:
 
395
                newpath = join(*([resolved] + bits[i:]))
 
396
                return realpath(newpath)
 
397
 
 
398
    return abspath(filename)
 
399
 
 
400
 
 
401
def _resolve_link(path):
 
402
    """Internal helper function.  Takes a path and follows symlinks
 
403
    until we either arrive at something that isn't a symlink, or
 
404
    encounter a path we've seen before (meaning that there's a loop).
 
405
    """
 
406
    paths_seen = set()
 
407
    while islink(path):
 
408
        if path in paths_seen:
 
409
            # Already seen this path, so we must have a symlink loop
 
410
            return None
 
411
        paths_seen.add(path)
 
412
        # Resolve where the link points to
 
413
        resolved = os.readlink(path)
 
414
        if not isabs(resolved):
 
415
            dir = dirname(path)
 
416
            path = normpath(join(dir, resolved))
 
417
        else:
 
418
            path = normpath(resolved)
 
419
    return path
 
420
 
 
421
supports_unicode_filenames = False
 
422
 
 
423
def relpath(path, start=None):
 
424
    """Return a relative version of a path"""
 
425
 
 
426
    if not path:
 
427
        raise ValueError("no path specified")
 
428
 
 
429
    if isinstance(path, bytes):
 
430
        curdir = b'.'
 
431
        sep = b'/'
 
432
        pardir = b'..'
 
433
    else:
 
434
        curdir = '.'
 
435
        sep = '/'
 
436
        pardir = '..'
 
437
 
 
438
    if start is None:
 
439
        start = curdir
 
440
 
 
441
    start_list = abspath(start).split(sep)
 
442
    path_list = abspath(path).split(sep)
 
443
 
 
444
    # Work out how much of the filepath is shared by start and path.
 
445
    i = len(commonprefix([start_list, path_list]))
 
446
 
 
447
    rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
 
448
    if not rel_list:
 
449
        return curdir
 
450
    return join(*rel_list)