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

Delta Between Two Patch Sets: 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/
Left Patch Set: Victor's latest patch (Sept 29 '08) Created 1 year, 1 month ago , Downloaded from: http://bugs.python.org/file11658/python3_bytes_filename.patch
Right 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:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
1 """Common operations on Posix pathnames. 1 """Common operations on Posix pathnames.
GvR 2008/09/30 18:10:41 Also note that os.path.dirname() and os.path.basen
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'
35 37
38 def _get_sep(path):
39 if isinstance(path, bytes):
40 return b'/'
41 else:
42 return '/'
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."""
62 if isinstance(a, bytes): 72 sep = _get_sep(a)
63 sep = b'/'
64 else:
65 sep = '/'
66 path = a 73 path = a
67 for b in p: 74 for b in p:
68 if b.startswith(sep): 75 if b.startswith(sep):
69 path = b 76 path = b
70 elif not path or path.endswith(sep): 77 elif not path or path.endswith(sep):
71 path += b 78 path += b
72 else: 79 else:
73 path += sep + b 80 path += sep + b
74 return path 81 return path
75 82
76 83
77 # 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
78 # 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
79 # '/' in the path, head will be empty. 86 # '/' in the path, head will be empty.
80 # Trailing '/'es are stripped from head unless it is the root. 87 # Trailing '/'es are stripped from head unless it is the root.
81 88
82 def split(p): 89 def split(p):
83 """Split a pathname. Returns tuple "(head, tail)" where "tail" is 90 """Split a pathname. Returns tuple "(head, tail)" where "tail" is
84 everything after the final slash. Either part may be empty.""" 91 everything after the final slash. Either part may be empty."""
85 if isinstance(p, bytes): 92 sep = _get_sep(p)
86 sep = b'/'
87 else:
88 sep = '/'
89 i = p.rfind(sep) + 1 93 i = p.rfind(sep) + 1
90 head, tail = p[:i], p[i:] 94 head, tail = p[:i], p[i:]
91 if head and head != sep*len(head): 95 if head and head != sep*len(head):
92 head = head.rstrip(sep) 96 head = head.rstrip(sep)
93 return head, tail 97 return head, tail
94 98
95 99
96 # Split a path in root and extension. 100 # Split a path in root and extension.
97 # 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
98 # pathname component; the root is everything before that. 102 # pathname component; the root is everything before that.
99 # It is always true that root + ext == p. 103 # It is always true that root + ext == p.
100 104
101 def splitext(p): 105 def splitext(p):
102 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)
103 splitext.__doc__ = genericpath._splitext.__doc__ 113 splitext.__doc__ = genericpath._splitext.__doc__
104 114
105 # 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
106 # 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.
107 117
108 def splitdrive(p): 118 def splitdrive(p):
109 """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
110 empty.""" 120 empty."""
111 return '', p 121 return p[:0], p
112 122
113 123
114 # 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].
115 125
116 def basename(p): 126 def basename(p):
117 """Returns the final component of a pathname""" 127 """Returns the final component of a pathname"""
118 i = p.rfind('/') + 1 128 sep = _get_sep(p)
129 i = p.rfind(sep) + 1
119 return p[i:] 130 return p[i:]
120 131
121 132
122 # 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].
123 134
124 def dirname(p): 135 def dirname(p):
125 """Returns the directory component of a pathname""" 136 """Returns the directory component of a pathname"""
126 i = p.rfind('/') + 1 137 sep = _get_sep(p)
138 i = p.rfind(sep) + 1
127 head = p[:i] 139 head = p[:i]
128 if head and head != '/'*len(head): 140 if head and head != sep*len(head):
129 head = head.rstrip('/') 141 head = head.rstrip(sep)
130 return head 142 return head
131 143
132 144
133 # Is a path a symbolic link? 145 # Is a path a symbolic link?
134 # 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.
135 147
136 def islink(path): 148 def islink(path):
137 """Test whether a path is a symbolic link""" 149 """Test whether a path is a symbolic link"""
138 try: 150 try:
139 st = os.lstat(path) 151 st = os.lstat(path)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
180 s1.st_dev == s2.st_dev 192 s1.st_dev == s2.st_dev
181 193
182 194
183 # Is a path a mount point? 195 # Is a path a mount point?
184 # (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?)
185 197
186 def ismount(path): 198 def ismount(path):
187 """Test whether a path is a mount point""" 199 """Test whether a path is a mount point"""
188 try: 200 try:
189 s1 = os.lstat(path) 201 s1 = os.lstat(path)
190 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)
191 except os.error: 207 except os.error:
192 return False # It doesn't exist -- so not a mount point :-) 208 return False # It doesn't exist -- so not a mount point :-)
193 dev1 = s1.st_dev 209 dev1 = s1.st_dev
194 dev2 = s2.st_dev 210 dev2 = s2.st_dev
195 if dev1 != dev2: 211 if dev1 != dev2:
196 return True # path/.. on a different device as path 212 return True # path/.. on a different device as path
197 ino1 = s1.st_ino 213 ino1 = s1.st_ino
198 ino2 = s2.st_ino 214 ino2 = s2.st_ino
199 if ino1 == ino2: 215 if ino1 == ino2:
200 return True # path/.. is the same i-node as path 216 return True # path/.. is the same i-node as path
201 return False 217 return False
202 218
203 219
204 # Expand paths beginning with '~' or '~user'. 220 # Expand paths beginning with '~' or '~user'.
205 # '~' means $HOME; '~user' means that user's home directory. 221 # '~' means $HOME; '~user' means that user's home directory.
206 # 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,
207 # the path is returned unchanged (leaving error reporting to whatever 223 # the path is returned unchanged (leaving error reporting to whatever
208 # function is called with the expanded path as argument). 224 # function is called with the expanded path as argument).
209 # See also module 'glob' for expansion of *, ? and [...] in pathnames. 225 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
210 # (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
211 # variable expansion.) 227 # variable expansion.)
212 228
213 def expanduser(path): 229 def expanduser(path):
214 """Expand ~ and ~user constructions. If user or $HOME is unknown, 230 """Expand ~ and ~user constructions. If user or $HOME is unknown,
215 do nothing.""" 231 do nothing."""
216 if not path.startswith('~'): 232 if isinstance(path, bytes):
233 tilde = b'~'
234 else:
235 tilde = '~'
236 if not path.startswith(tilde):
217 return path 237 return path
218 i = path.find('/', 1) 238 sep = _get_sep(path)
239 i = path.find(sep, 1)
219 if i < 0: 240 if i < 0:
220 i = len(path) 241 i = len(path)
221 if i == 1: 242 if i == 1:
222 if 'HOME' not in os.environ: 243 if 'HOME' not in os.environ:
223 import pwd 244 import pwd
224 userhome = pwd.getpwuid(os.getuid()).pw_dir 245 userhome = pwd.getpwuid(os.getuid()).pw_dir
225 else: 246 else:
226 userhome = os.environ['HOME'] 247 userhome = os.environ['HOME']
227 else: 248 else:
228 import pwd 249 import pwd
250 name = path[1:i]
251 if isinstance(name, bytes):
252 name = str(name, 'ASCII')
229 try: 253 try:
230 pwent = pwd.getpwnam(path[1:i]) 254 pwent = pwd.getpwnam(name)
231 except KeyError: 255 except KeyError:
232 return path 256 return path
233 userhome = pwent.pw_dir 257 userhome = pwent.pw_dir
234 userhome = userhome.rstrip('/') 258 if isinstance(path, bytes):
259 userhome = userhome.encode(sys.getfilesystemencoding())
260 userhome = userhome.rstrip(sep)
235 return userhome + path[i:] 261 return userhome + path[i:]
236 262
237 263
238 # Expand paths containing shell variable substitutions. 264 # Expand paths containing shell variable substitutions.
239 # This expands the forms $variable and ${variable} only. 265 # This expands the forms $variable and ${variable} only.
240 # Non-existent variables are left unchanged. 266 # Non-existent variables are left unchanged.
241 267
242 _varprog = None 268 _varprog = None
269 _varprogb = None
243 270
244 def expandvars(path): 271 def expandvars(path):
245 """Expand shell variables of form $var and ${var}. Unknown variables 272 """Expand shell variables of form $var and ${var}. Unknown variables
246 are left unchanged.""" 273 are left unchanged."""
247 global _varprog 274 global _varprog, _varprogb
248 if '$' not in path: 275 if isinstance(path, bytes):
249 return path 276 if b'$' not in path:
250 if not _varprog: 277 return path
251 import re 278 if not _varprogb:
252 _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 = '}'
253 i = 0 293 i = 0
254 while True: 294 while True:
255 m = _varprog.search(path, i) 295 m = search(path, i)
256 if not m: 296 if not m:
257 break 297 break
258 i, j = m.span(0) 298 i, j = m.span(0)
259 name = m.group(1) 299 name = m.group(1)
260 if name.startswith('{') and name.endswith('}'): 300 if name.startswith(start) and name.endswith(end):
261 name = name[1:-1] 301 name = name[1:-1]
302 if isinstance(name, bytes):
303 name = str(name, 'ASCII')
262 if name in os.environ: 304 if name in os.environ:
263 tail = path[j:] 305 tail = path[j:]
264 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
265 i = len(path) 310 i = len(path)
266 path += tail 311 path += tail
267 else: 312 else:
268 i = j 313 i = j
269 return path 314 return path
270 315
271 316
272 # 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.
273 # 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
274 # if it contains symbolic links! 319 # if it contains symbolic links!
275 320
276 def normpath(path): 321 def normpath(path):
277 """Normalize path, eliminating double slashes, etc.""" 322 """Normalize path, eliminating double slashes, etc."""
278 if path == '': 323 if isinstance(path, bytes):
279 return '.' 324 sep = b'/'
280 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)
281 # 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
282 # as single slash. 337 # as single slash.
283 if (initial_slashes and 338 if (initial_slashes and
284 path.startswith('//') and not path.startswith('///')): 339 path.startswith(sep*2) and not path.startswith(sep*3)):
285 initial_slashes = 2 340 initial_slashes = 2
286 comps = path.split('/') 341 comps = path.split(sep)
287 new_comps = [] 342 new_comps = []
288 for comp in comps: 343 for comp in comps:
289 if comp in ('', '.'): 344 if comp in (empty, dot):
290 continue 345 continue
291 if (comp != '..' or (not initial_slashes and not new_comps) or 346 if (comp != dotdot or (not initial_slashes and not new_comps) or
292 (new_comps and new_comps[-1] == '..')): 347 (new_comps and new_comps[-1] == dotdot)):
293 new_comps.append(comp) 348 new_comps.append(comp)
294 elif new_comps: 349 elif new_comps:
295 new_comps.pop() 350 new_comps.pop()
296 comps = new_comps 351 comps = new_comps
297 path = '/'.join(comps) 352 path = sep.join(comps)
298 if initial_slashes: 353 if initial_slashes:
299 path = '/'*initial_slashes + path 354 path = sep*initial_slashes + path
300 return path or '.' 355 return path or dot
301 356
302 357
303 def abspath(path): 358 def abspath(path):
304 """Return an absolute path.""" 359 """Return an absolute path."""
305 if not isabs(path): 360 if not isabs(path):
306 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)
307 return normpath(path) 366 return normpath(path)
308 367
309 368
310 # 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
311 # filesystem). 370 # filesystem).
312 371
313 def realpath(filename): 372 def realpath(filename):
314 """Return the canonical path of the specified filename, eliminating any 373 """Return the canonical path of the specified filename, eliminating any
315 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 = ''
316 if isabs(filename): 381 if isabs(filename):
317 bits = ['/'] + filename.split('/')[1:] 382 bits = [sep] + filename.split(sep)[1:]
318 else: 383 else:
319 bits = [''] + filename.split('/') 384 bits = [empty] + filename.split(sep)
320 385
321 for i in range(2, len(bits)+1): 386 for i in range(2, len(bits)+1):
322 component = join(*bits[0:i]) 387 component = join(*bits[0:i])
323 # Resolve symbolic links. 388 # Resolve symbolic links.
324 if islink(component): 389 if islink(component):
325 resolved = _resolve_link(component) 390 resolved = _resolve_link(component)
326 if resolved is None: 391 if resolved is None:
327 # Infinite loop -- return original component + rest of the path 392 # Infinite loop -- return original component + rest of the path
328 return abspath(join(*([component] + bits[i:]))) 393 return abspath(join(*([component] + bits[i:])))
329 else: 394 else:
(...skipping 18 matching lines...) Expand all
348 resolved = os.readlink(path) 413 resolved = os.readlink(path)
349 if not isabs(resolved): 414 if not isabs(resolved):
350 dir = dirname(path) 415 dir = dirname(path)
351 path = normpath(join(dir, resolved)) 416 path = normpath(join(dir, resolved))
352 else: 417 else:
353 path = normpath(resolved) 418 path = normpath(resolved)
354 return path 419 return path
355 420
356 supports_unicode_filenames = False 421 supports_unicode_filenames = False
357 422
358 def relpath(path, start=curdir): 423 def relpath(path, start=None):
359 """Return a relative version of a path""" 424 """Return a relative version of a path"""
360 425
361 if not path: 426 if not path:
362 raise ValueError("no path specified") 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
363 440
364 start_list = abspath(start).split(sep) 441 start_list = abspath(start).split(sep)
365 path_list = abspath(path).split(sep) 442 path_list = abspath(path).split(sep)
366 443
367 # 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.
368 i = len(commonprefix([start_list, path_list])) 445 i = len(commonprefix([start_list, path_list]))
369 446
370 rel_list = [pardir] * (len(start_list)-i) + path_list[i:] 447 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
371 if not rel_list: 448 if not rel_list:
372 return curdir 449 return curdir
373 return join(*rel_list) 450 return join(*rel_list)
LEFTRIGHT

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