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