Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(65)

Side by Side Diff: Lib/posixpath.py

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

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld r497