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 stat
15
16 __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
17 "basename","dirname","commonprefix","getsize","getmtime",
18 "getatime","getctime","islink","exists","lexists","isdir","isfile",
19 "ismount","walk","expanduser","expandvars","normpath","abspath",
20 "samefile","sameopenfile","samestat",
21 "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
22 "devnull","realpath","supports_unicode_filenames"]
23
24
25 curdir = '.'
26 pardir = '..'
27 extsep = '.'
28 sep = '/'
29 pathsep = ':'
30 defpath = ':/bin:/usr/bin'
31 altsep = None
32 devnull = '/dev/null'
33
34
35
36
37
38
40 """Normalize case of pathname. Has no effect under Posix"""
41 return s
42
43
44
45
46
48 """Test whether a path is absolute"""
49 return s.startswith('/')
50
51
52
53
54
55
57 """Join two or more pathname components, inserting '/' as needed"""
58 path = a
59 for b in p:
60 if b.startswith('/'):
61 path = b
62 elif path == '' or path.endswith('/'):
63 path += b
64 else:
65 path += '/' + b
66 return path
67
68
69
70
71
72
73
75 """Split a pathname. Returns tuple "(head, tail)" where "tail" is
76 everything after the final slash. Either part may be empty."""
77 i = p.rfind('/') + 1
78 head, tail = p[:i], p[i:]
79 if head and head != '/'*len(head):
80 head = head.rstrip('/')
81 return head, tail
82
83
84
85
86
87
88
90 """Split the extension from a pathname. Extension is everything from the
91 last dot to the end. Returns "(root, ext)", either part may be empty."""
92 i = p.rfind('.')
93 if i<=p.rfind('/'):
94 return p, ''
95 else:
96 return p[:i], p[i:]
97
98
99
100
101
103 """Split a pathname into drive and path. On Posix, drive is always
104 empty."""
105 return '', p
106
107
108
109
111 """Returns the final component of a pathname"""
112 return split(p)[1]
113
114
115
116
118 """Returns the directory component of a pathname"""
119 return split(p)[0]
120
121
122
123
125 "Given a list of pathnames, returns the longest common leading component"
126 if not m: return ''
127 s1 = min(m)
128 s2 = max(m)
129 n = min(len(s1), len(s2))
130 for i in xrange(n):
131 if s1[i] != s2[i]:
132 return s1[:i]
133 return s1[:n]
134
135
136
138 """Return the size of a file, reported by os.stat()."""
139 return os.stat(filename).st_size
140
142 """Return the last modification time of a file, reported by os.stat()."""
143 return os.stat(filename).st_mtime
144
146 """Return the last access time of a file, reported by os.stat()."""
147 return os.stat(filename).st_atime
148
150 """Return the metadata change time of a file, reported by os.stat()."""
151 return os.stat(filename).st_ctime
152
153
154
155
157 """Test whether a path is a symbolic link"""
158 try:
159 st = os.lstat(path)
160 except (os.error, AttributeError):
161 return False
162 return stat.S_ISLNK(st.st_mode)
163
164
165
166
167
169 """Test whether a path exists. Returns False for broken symbolic links"""
170 try:
171 st = os.stat(path)
172 except os.error:
173 return False
174 return True
175
176
177
178
180 """Test whether a path exists. Returns True for broken symbolic links"""
181 try:
182 st = os.lstat(path)
183 except os.error:
184 return False
185 return True
186
187
188
189
190
191
193 """Test whether a path is a directory"""
194 try:
195 st = os.stat(path)
196 except os.error:
197 return False
198 return stat.S_ISDIR(st.st_mode)
199
200
201
202
203
204
206 """Test whether a path is a regular file"""
207 try:
208 st = os.stat(path)
209 except os.error:
210 return False
211 return stat.S_ISREG(st.st_mode)
212
213
214
215
217 """Test whether two pathnames reference the same actual file"""
218 s1 = os.stat(f1)
219 s2 = os.stat(f2)
220 return samestat(s1, s2)
221
222
223
224
225
227 """Test whether two open file objects reference the same file"""
228 s1 = os.fstat(fp1)
229 s2 = os.fstat(fp2)
230 return samestat(s1, s2)
231
232
233
234
235
237 """Test whether two stat buffers reference the same file"""
238 return s1.st_ino == s2.st_ino and \
239 s1.st_dev == s2.st_dev
240
241
242
243
244
246 """Test whether a path is a mount point"""
247 try:
248 s1 = os.lstat(path)
249 s2 = os.lstat(join(path, '..'))
250 except os.error:
251 return False
252 dev1 = s1.st_dev
253 dev2 = s2.st_dev
254 if dev1 != dev2:
255 return True
256 ino1 = s1.st_ino
257 ino2 = s2.st_ino
258 if ino1 == ino2:
259 return True
260 return False
261
262
263
264
265
266
267
268
269
270
271 -def walk(top, func, arg):
272 """Directory tree walk with callback function.
273
274 For each directory in the directory tree rooted at top (including top
275 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
276 dirname is the name of the directory, and fnames a list of the names of
277 the files and subdirectories in dirname (excluding '.' and '..'). func
278 may modify the fnames list in-place (e.g. via del or slice assignment),
279 and walk will only recurse into the subdirectories whose names remain in
280 fnames; this can be used to implement a filter, or to impose a specific
281 order of visiting. No semantics are defined for, or required of, arg,
282 beyond that arg is always passed to func. It can be used, e.g., to pass
283 a filename pattern, or a mutable object designed to accumulate
284 statistics. Passing None for arg is common."""
285
286 try:
287 names = os.listdir(top)
288 except os.error:
289 return
290 func(arg, top, names)
291 for name in names:
292 name = join(top, name)
293 try:
294 st = os.lstat(name)
295 except os.error:
296 continue
297 if stat.S_ISDIR(st.st_mode):
298 walk(name, func, arg)
299
300
301
302
303
304
305
306
307
308
309
311 """Expand ~ and ~user constructions. If user or $HOME is unknown,
312 do nothing."""
313 if not path.startswith('~'):
314 return path
315 i = path.find('/', 1)
316 if i < 0:
317 i = len(path)
318 if i == 1:
319 if 'HOME' not in os.environ:
320 import pwd
321 userhome = pwd.getpwuid(os.getuid()).pw_dir
322 else:
323 userhome = os.environ['HOME']
324 else:
325 import pwd
326 try:
327 pwent = pwd.getpwnam(path[1:i])
328 except KeyError:
329 return path
330 userhome = pwent.pw_dir
331 userhome = userhome.rstrip('/')
332 return userhome + path[i:]
333
334
335
336
337
338
339 _varprog = None
340
342 """Expand shell variables of form $var and ${var}. Unknown variables
343 are left unchanged."""
344 global _varprog
345 if '$' not in path:
346 return path
347 if not _varprog:
348 import re
349 _varprog = re.compile(r'\$(\w+|\{[^}]*\})')
350 i = 0
351 while True:
352 m = _varprog.search(path, i)
353 if not m:
354 break
355 i, j = m.span(0)
356 name = m.group(1)
357 if name.startswith('{') and name.endswith('}'):
358 name = name[1:-1]
359 if name in os.environ:
360 tail = path[j:]
361 path = path[:i] + os.environ[name]
362 i = len(path)
363 path += tail
364 else:
365 i = j
366 return path
367
368
369
370
371
372
374 """Normalize path, eliminating double slashes, etc."""
375 if path == '':
376 return '.'
377 initial_slashes = path.startswith('/')
378
379
380 if (initial_slashes and
381 path.startswith('//') and not path.startswith('///')):
382 initial_slashes = 2
383 comps = path.split('/')
384 new_comps = []
385 for comp in comps:
386 if comp in ('', '.'):
387 continue
388 if (comp != '..' or (not initial_slashes and not new_comps) or
389 (new_comps and new_comps[-1] == '..')):
390 new_comps.append(comp)
391 elif new_comps:
392 new_comps.pop()
393 comps = new_comps
394 path = '/'.join(comps)
395 if initial_slashes:
396 path = '/'*initial_slashes + path
397 return path or '.'
398
399
405
406
407
408
409
411 """Return the canonical path of the specified filename, eliminating any
412 symbolic links encountered in the path."""
413 if isabs(filename):
414 bits = ['/'] + filename.split('/')[1:]
415 else:
416 bits = [''] + filename.split('/')
417
418 for i in range(2, len(bits)+1):
419 component = join(*bits[0:i])
420
421 if islink(component):
422 resolved = _resolve_link(component)
423 if resolved is None:
424
425 return abspath(join(*([component] + bits[i:])))
426 else:
427 newpath = join(*([resolved] + bits[i:]))
428 return realpath(newpath)
429
430 return abspath(filename)
431
432
434 """Internal helper function. Takes a path and follows symlinks
435 until we either arrive at something that isn't a symlink, or
436 encounter a path we've seen before (meaning that there's a loop).
437 """
438 paths_seen = []
439 while islink(path):
440 if path in paths_seen:
441
442 return None
443 paths_seen.append(path)
444
445 resolved = os.readlink(path)
446 if not isabs(resolved):
447 dir = dirname(path)
448 path = normpath(join(dir, resolved))
449 else:
450 path = normpath(resolved)
451 return path
452
453 supports_unicode_filenames = False
454