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

Delta Between Two Patch Sets: Lib/ntpath.py

Issue 10395: new os.path function to extract common prefix based on path components
Left Patch Set: Created 6 years, 8 months ago
Right Patch Set: Created 5 years ago
Left:
Right:
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
« no previous file with change/comment | « Doc/library/os.path.rst ('k') | Lib/posixpath.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # Module 'ntpath' -- common operations on WinNT/Win95 pathnames 1 # Module 'ntpath' -- common operations on WinNT/Win95 pathnames
2 """Common pathname manipulations, WindowsNT/95 version. 2 """Common pathname manipulations, WindowsNT/95 version.
3 3
4 Instead of importing this module directly, import os and refer to this 4 Instead of importing this module directly, import os and refer to this
5 module as os.path. 5 module as os.path.
6 """ 6 """
7 7
8 import os 8 import os
9 import sys 9 import sys
10 import stat 10 import stat
11 import genericpath 11 import genericpath
12 from genericpath import * 12 from genericpath import *
13 13
14 __all__ = ["normcase","isabs","join","splitdrive","split","splitext", 14 __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
15 "basename","dirname","commonprefix","getsize","getmtime", 15 "basename","dirname","commonprefix","getsize","getmtime",
16 "getatime","getctime", "islink","exists","lexists","isdir","isfile", 16 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
17 "ismount", "expanduser","expandvars","normpath","abspath", 17 "ismount", "expanduser","expandvars","normpath","abspath",
18 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", 18 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
19 "extsep","devnull","realpath","supports_unicode_filenames","relpath", 19 "extsep","devnull","realpath","supports_unicode_filenames","relpath",
20 "samefile", "sameopenfile", 'commonpath'] 20 "samefile", "sameopenfile", "samestat", "commonpath"]
21 21
22 # strings representing various path-related bits and pieces 22 # strings representing various path-related bits and pieces
23 # These are primarily for export; internally, they are hardcoded. 23 # These are primarily for export; internally, they are hardcoded.
24 curdir = '.' 24 curdir = '.'
25 pardir = '..' 25 pardir = '..'
26 extsep = '.' 26 extsep = '.'
27 sep = '\\' 27 sep = '\\'
28 pathsep = ';' 28 pathsep = ';'
29 altsep = '/' 29 altsep = '/'
30 defpath = '.;C:\\bin' 30 defpath = '.;C:\\bin'
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 # volume), or if a pathname after the volume-letter-and-colon or UNC-resource 94 # volume), or if a pathname after the volume-letter-and-colon or UNC-resource
95 # starts with a slash or backslash. 95 # starts with a slash or backslash.
96 96
97 def isabs(s): 97 def isabs(s):
98 """Test whether a path is absolute""" 98 """Test whether a path is absolute"""
99 s = splitdrive(s)[1] 99 s = splitdrive(s)[1]
100 return len(s) > 0 and s[:1] in _get_bothseps(s) 100 return len(s) > 0 and s[:1] in _get_bothseps(s)
101 101
102 102
103 # Join two (or more) paths. 103 # Join two (or more) paths.
104 104 def join(path, *paths):
105 def join(a, *p): 105 sep = _get_sep(path)
106 """Join two or more pathname components, inserting "\\" as needed. 106 seps = _get_bothseps(path)
107 If any component is an absolute path, all previous path components 107 colon = _get_colon(path)
108 will be discarded.""" 108 result_drive, result_path = splitdrive(path)
109 sep = _get_sep(a) 109 for p in paths:
110 seps = _get_bothseps(a) 110 p_drive, p_path = splitdrive(p)
111 colon = _get_colon(a) 111 if p_path and p_path[0] in seps:
112 path = a 112 # Second path is absolute
113 for b in p: 113 if p_drive or not result_drive:
114 b_wins = 0 # set to 1 iff b makes path irrelevant 114 result_drive = p_drive
115 if not path: 115 result_path = p_path
116 b_wins = 1 116 continue
117 117 elif p_drive and p_drive != result_drive:
118 elif isabs(b): 118 if p_drive.lower() != result_drive.lower():
119 # This probably wipes out path so far. However, it's more 119 # Different drives => ignore the first path entirely
120 # complicated if path begins with a drive letter. You get a+b 120 result_drive = p_drive
121 # (minus redundant slashes) in these four cases: 121 result_path = p_path
122 # 1. join('c:', '/a') == 'c:/a' 122 continue
123 # 2. join('//computer/share', '/a') == '//computer/share/a' 123 # Same drive in different case
124 # 3. join('c:/', '/a') == 'c:/a' 124 result_drive = p_drive
125 # 4. join('//computer/share/', '/a') == '//computer/share/a' 125 # Second path is relative to the first
126 # But b wins in all of these cases: 126 if result_path and result_path[-1] not in seps:
127 # 5. join('c:/a', '/b') == '/b' 127 result_path = result_path + sep
128 # 6. join('//computer/share/a', '/b') == '/b' 128 result_path = result_path + p_path
129 # 7. join('c:', 'd:/') == 'd:/' 129 ## add separator between UNC and non-absolute path
130 # 8. join('c:', '//computer/share/') == '//computer/share/' 130 if (result_path and result_path[0] not in seps and
131 # 9. join('//computer/share', 'd:/') == 'd:/' 131 result_drive and result_drive[-1:] != colon):
132 # 10. join('//computer/share', '//computer/share/') == '//compute r/share/' 132 return result_drive + sep + result_path
133 # 11. join('c:/', 'd:/') == 'd:/' 133 return result_drive + result_path
134 # 12. join('c:/', '//computer/share/') == '//computer/share/'
135 # 13. join('//computer/share/', 'd:/') == 'd:/'
136 # 14. join('//computer/share/', '//computer/share/') == '//comput er/share/'
137 b_prefix, b_rest = splitdrive(b)
138
139 # if b has a prefix, it always wins.
140 if b_prefix:
141 b_wins = 1
142 else:
143 # b doesn't have a prefix.
144 # but isabs(b) returned true.
145 # and therefore b_rest[0] must be a slash.
146 # (but let's check that.)
147 assert(b_rest and b_rest[0] in seps)
148
149 # so, b still wins if path has a rest that's more than a sep.
150 # you get a+b if path_rest is empty or only has a sep.
151 # (see cases 1-4 for times when b loses.)
152 path_rest = splitdrive(path)[1]
153 b_wins = path_rest and path_rest not in seps
154
155 if b_wins:
156 path = b
157 else:
158 # Join, and ensure there's a separator.
159 assert len(path) > 0
160 if path[-1:] in seps:
161 if b and b[:1] in seps:
162 path += b[1:]
163 else:
164 path += b
165 elif path[-1:] == colon:
166 path += b
167 elif b:
168 if b[:1] in seps:
169 path += b
170 else:
171 path += sep + b
172 else:
173 # path is not empty and does not end with a backslash,
174 # but b is empty; since, e.g., split('a/') produces
175 # ('a', ''), it's best if join() adds a backslash in
176 # this case.
177 path += sep
178
179 return path
180 134
181 135
182 # Split a path in a drive specification (a drive letter followed by a 136 # Split a path in a drive specification (a drive letter followed by a
183 # colon) and the path specification. 137 # colon) and the path specification.
184 # It is always true that drivespec + pathspec == p 138 # It is always true that drivespec + pathspec == p
185 def splitdrive(p): 139 def splitdrive(p):
186 """Split a pathname into drive/UNC sharepoint and relative path specifiers. 140 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
187 Returns a 2-tuple (drive_or_unc, path); either part may be empty. 141 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
188 142
189 If you assign 143 If you assign
190 result = splitdrive(p) 144 result = splitdrive(p)
191 It is always true that: 145 It is always true that:
192 result[0] + result[1] == p 146 result[0] + result[1] == p
193 147
194 If the path contained a drive letter, drive_or_unc will contain everything 148 If the path contained a drive letter, drive_or_unc will contain everything
195 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/d ir") 149 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/d ir")
196 150
197 If the path contained a UNC path, the drive_or_unc will contain the host nam e 151 If the path contained a UNC path, the drive_or_unc will contain the host nam e
198 and share up to but not including the fourth directory separator character. 152 and share up to but not including the fourth directory separator character.
199 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir") 153 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
200 154
201 Paths cannot contain both a drive letter and a UNC path. 155 Paths cannot contain both a drive letter and a UNC path.
202 156
203 """ 157 """
204 empty = _get_empty(p) 158 empty = _get_empty(p)
205 if len(p) > 1: 159 if len(p) > 1:
206 sep = _get_sep(p) 160 sep = _get_sep(p)
207 normp = normcase(p) 161 normp = p.replace(_get_altsep(p), sep)
208 if (normp[0:2] == sep*2) and (normp[2:3] != sep): 162 if (normp[0:2] == sep*2) and (normp[2:3] != sep):
209 # is a UNC path: 163 # is a UNC path:
210 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path 164 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
211 # \\machine\mountpoint\directory\etc\... 165 # \\machine\mountpoint\directory\etc\...
212 # directory ^^^^^^^^^^^^^^^ 166 # directory ^^^^^^^^^^^^^^^
213 index = normp.find(sep, 2) 167 index = normp.find(sep, 2)
214 if index == -1: 168 if index == -1:
215 return empty, p 169 return empty, p
216 index2 = normp.find(sep, index + 1) 170 index2 = normp.find(sep, index + 1)
217 # a UNC path can't have two slashes in a row 171 # a UNC path can't have two slashes in a row
(...skipping 15 matching lines...) Expand all
233 187
234 Split a pathname into UNC mount point and relative path specifiers. 188 Split a pathname into UNC mount point and relative path specifiers.
235 189
236 Return a 2-tuple (unc, rest); either part may be empty. 190 Return a 2-tuple (unc, rest); either part may be empty.
237 If unc is not empty, it has the form '//host/mount' (or similar 191 If unc is not empty, it has the form '//host/mount' (or similar
238 using backslashes). unc+rest is always the input path. 192 using backslashes). unc+rest is always the input path.
239 Paths containing drive letters never have an UNC part. 193 Paths containing drive letters never have an UNC part.
240 """ 194 """
241 import warnings 195 import warnings
242 warnings.warn("ntpath.splitunc is deprecated, use ntpath.splitdrive instead" , 196 warnings.warn("ntpath.splitunc is deprecated, use ntpath.splitdrive instead" ,
243 DeprecationWarning) 197 DeprecationWarning, 2)
244 sep = _get_sep(p) 198 drive, path = splitdrive(p)
245 if not p[1:2]: 199 if len(drive) == 2:
246 return p[:0], p # Drive letter present 200 # Drive letter present
247 firstTwo = p[0:2] 201 return p[:0], p
248 if normcase(firstTwo) == sep + sep: 202 return drive, path
249 # is a UNC path:
250 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
251 # \\machine\mountpoint\directories...
252 # directory ^^^^^^^^^^^^^^^
253 normp = normcase(p)
254 index = normp.find(sep, 2)
255 if index == -1:
256 ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
257 return (p[:0], p)
258 index = normp.find(sep, index + 1)
259 if index == -1:
260 index = len(p)
261 return p[:index], p[index:]
262 return p[:0], p
263 203
264 204
265 # Split a path in head (everything up to the last '/') and tail (the 205 # Split a path in head (everything up to the last '/') and tail (the
266 # rest). After the trailing '/' is stripped, the invariant 206 # rest). After the trailing '/' is stripped, the invariant
267 # join(head, tail) == p holds. 207 # join(head, tail) == p holds.
268 # The resulting head won't end in '/' unless it is the root. 208 # The resulting head won't end in '/' unless it is the root.
269 209
270 def split(p): 210 def split(p):
271 """Split a pathname. 211 """Split a pathname.
272 212
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 254
315 # Is a path a symbolic link? 255 # Is a path a symbolic link?
316 # This will always return false on systems where os.lstat doesn't exist. 256 # This will always return false on systems where os.lstat doesn't exist.
317 257
318 def islink(path): 258 def islink(path):
319 """Test whether a path is a symbolic link. 259 """Test whether a path is a symbolic link.
320 This will always return false for Windows prior to 6.0. 260 This will always return false for Windows prior to 6.0.
321 """ 261 """
322 try: 262 try:
323 st = os.lstat(path) 263 st = os.lstat(path)
324 except (os.error, AttributeError): 264 except (OSError, AttributeError):
325 return False 265 return False
326 return stat.S_ISLNK(st.st_mode) 266 return stat.S_ISLNK(st.st_mode)
327 267
328 # Being true for dangling symbolic links is also useful. 268 # Being true for dangling symbolic links is also useful.
329 269
330 def lexists(path): 270 def lexists(path):
331 """Test whether a path exists. Returns True for broken symbolic links""" 271 """Test whether a path exists. Returns True for broken symbolic links"""
332 try: 272 try:
333 st = os.lstat(path) 273 st = os.lstat(path)
334 except (os.error, WindowsError): 274 except OSError:
335 return False 275 return False
336 return True 276 return True
337 277
338 # Is a path a mount point? Either a root (with or without drive letter) 278 # Is a path a mount point?
339 # or an UNC path with at most a / or \ after the mount point. 279 # Any drive letter root (eg c:\)
340 280 # Any share UNC (eg \\server\share)
281 # Any volume mounted on a filesystem folder
282 #
283 # No one method detects all three situations. Historically we've lexically
284 # detected drive letter roots and share UNCs. The canonical approach to
285 # detecting mounted volumes (querying the reparse tag) fails for the most
286 # common case: drive letter roots. The alternative which uses GetVolumePathName
287 # fails if the drive letter is the result of a SUBST.
288 try:
289 from nt import _getvolumepathname
290 except ImportError:
291 _getvolumepathname = None
341 def ismount(path): 292 def ismount(path):
342 """Test whether a path is a mount point (defined as root of drive)""" 293 """Test whether a path is a mount point (a drive root, the root of a
294 share, or a mounted volume)"""
343 seps = _get_bothseps(path) 295 seps = _get_bothseps(path)
296 path = abspath(path)
344 root, rest = splitdrive(path) 297 root, rest = splitdrive(path)
345 if root and root[0] in seps: 298 if root and root[0] in seps:
346 return (not rest) or (rest in seps) 299 return (not rest) or (rest in seps)
347 return rest in seps 300 if rest in seps:
301 return True
302
303 if _getvolumepathname:
304 return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
305 else:
306 return False
348 307
349 308
350 # Expand paths beginning with '~' or '~user'. 309 # Expand paths beginning with '~' or '~user'.
351 # '~' means $HOME; '~user' means that user's home directory. 310 # '~' means $HOME; '~user' means that user's home directory.
352 # If the path doesn't begin with '~', or if the user or $HOME is unknown, 311 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
353 # the path is returned unchanged (leaving error reporting to whatever 312 # the path is returned unchanged (leaving error reporting to whatever
354 # function is called with the expanded path as argument). 313 # function is called with the expanded path as argument).
355 # See also module 'glob' for expansion of *, ? and [...] in pathnames. 314 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
356 # (A function should also be defined to do full *sh-style environment 315 # (A function should also be defined to do full *sh-style environment
357 # variable expansion.) 316 # variable expansion.)
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
411 Unknown variables are left unchanged.""" 370 Unknown variables are left unchanged."""
412 if isinstance(path, bytes): 371 if isinstance(path, bytes):
413 if ord('$') not in path and ord('%') not in path: 372 if ord('$') not in path and ord('%') not in path:
414 return path 373 return path
415 import string 374 import string
416 varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii') 375 varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
417 quote = b'\'' 376 quote = b'\''
418 percent = b'%' 377 percent = b'%'
419 brace = b'{' 378 brace = b'{'
420 dollar = b'$' 379 dollar = b'$'
380 environ = getattr(os, 'environb', None)
421 else: 381 else:
422 if '$' not in path and '%' not in path: 382 if '$' not in path and '%' not in path:
423 return path 383 return path
424 import string 384 import string
425 varchars = string.ascii_letters + string.digits + '_-' 385 varchars = string.ascii_letters + string.digits + '_-'
426 quote = '\'' 386 quote = '\''
427 percent = '%' 387 percent = '%'
428 brace = '{' 388 brace = '{'
429 dollar = '$' 389 dollar = '$'
390 environ = os.environ
430 res = path[:0] 391 res = path[:0]
431 index = 0 392 index = 0
432 pathlen = len(path) 393 pathlen = len(path)
433 while index < pathlen: 394 while index < pathlen:
434 c = path[index:index+1] 395 c = path[index:index+1]
435 if c == quote: # no expansion within single quotes 396 if c == quote: # no expansion within single quotes
436 path = path[index + 1:] 397 path = path[index + 1:]
437 pathlen = len(path) 398 pathlen = len(path)
438 try: 399 try:
439 index = path.index(c) 400 index = path.index(c)
440 res += c + path[:index + 1] 401 res += c + path[:index + 1]
441 except ValueError: 402 except ValueError:
442 res += path 403 res += path
443 index = pathlen - 1 404 index = pathlen - 1
444 elif c == percent: # variable or '%' 405 elif c == percent: # variable or '%'
445 if path[index + 1:index + 2] == percent: 406 if path[index + 1:index + 2] == percent:
446 res += c 407 res += c
447 index += 1 408 index += 1
448 else: 409 else:
449 path = path[index+1:] 410 path = path[index+1:]
450 pathlen = len(path) 411 pathlen = len(path)
451 try: 412 try:
452 index = path.index(percent) 413 index = path.index(percent)
453 except ValueError: 414 except ValueError:
454 res += percent + path 415 res += percent + path
455 index = pathlen - 1 416 index = pathlen - 1
456 else: 417 else:
457 var = path[:index] 418 var = path[:index]
458 if isinstance(path, bytes): 419 try:
459 var = var.decode('ascii') 420 if environ is None:
460 if var in os.environ: 421 value = os.fsencode(os.environ[os.fsdecode(var)])
461 value = os.environ[var] 422 else:
462 else: 423 value = environ[var]
463 value = '%' + var + '%' 424 except KeyError:
464 if isinstance(path, bytes): 425 value = percent + var + percent
465 value = value.encode('ascii')
466 res += value 426 res += value
467 elif c == dollar: # variable or '$$' 427 elif c == dollar: # variable or '$$'
468 if path[index + 1:index + 2] == dollar: 428 if path[index + 1:index + 2] == dollar:
469 res += c 429 res += c
470 index += 1 430 index += 1
471 elif path[index + 1:index + 2] == brace: 431 elif path[index + 1:index + 2] == brace:
472 path = path[index+2:] 432 path = path[index+2:]
473 pathlen = len(path) 433 pathlen = len(path)
474 try: 434 try:
475 if isinstance(path, bytes): 435 if isinstance(path, bytes):
476 index = path.index(b'}') 436 index = path.index(b'}')
477 else: 437 else:
478 index = path.index('}') 438 index = path.index('}')
479 var = path[:index]
480 if isinstance(path, bytes):
481 var = var.decode('ascii')
482 if var in os.environ:
483 value = os.environ[var]
484 else:
485 value = '${' + var + '}'
486 if isinstance(path, bytes):
487 value = value.encode('ascii')
488 res += value
489 except ValueError: 439 except ValueError:
490 if isinstance(path, bytes): 440 if isinstance(path, bytes):
491 res += b'${' + path 441 res += b'${' + path
492 else: 442 else:
493 res += '${' + path 443 res += '${' + path
494 index = pathlen - 1 444 index = pathlen - 1
445 else:
446 var = path[:index]
447 try:
448 if environ is None:
449 value = os.fsencode(os.environ[os.fsdecode(var)])
450 else:
451 value = environ[var]
452 except KeyError:
453 if isinstance(path, bytes):
454 value = b'${' + var + b'}'
455 else:
456 value = '${' + var + '}'
457 res += value
495 else: 458 else:
496 var = '' 459 var = path[:0]
497 index += 1 460 index += 1
498 c = path[index:index + 1] 461 c = path[index:index + 1]
499 while c and c in varchars: 462 while c and c in varchars:
500 if isinstance(path, bytes): 463 var += c
501 var += c.decode('ascii')
502 else:
503 var += c
504 index += 1 464 index += 1
505 c = path[index:index + 1] 465 c = path[index:index + 1]
506 if var in os.environ: 466 try:
507 value = os.environ[var] 467 if environ is None:
508 else: 468 value = os.fsencode(os.environ[os.fsdecode(var)])
509 value = '$' + var 469 else:
510 if isinstance(path, bytes): 470 value = environ[var]
511 value = value.encode('ascii') 471 except KeyError:
472 value = dollar + var
512 res += value 473 res += value
513 if c: 474 if c:
514 index -= 1 475 index -= 1
515 else: 476 else:
516 res += c 477 res += c
517 index += 1 478 index += 1
518 return res 479 return res
519 480
520 481
521 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. 482 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 path = join(cwd, path) 538 path = join(cwd, path)
578 return normpath(path) 539 return normpath(path)
579 540
580 else: # use native Windows method on Windows 541 else: # use native Windows method on Windows
581 def abspath(path): 542 def abspath(path):
582 """Return the absolute version of a path.""" 543 """Return the absolute version of a path."""
583 544
584 if path: # Empty path must return current working directory. 545 if path: # Empty path must return current working directory.
585 try: 546 try:
586 path = _getfullpathname(path) 547 path = _getfullpathname(path)
587 except WindowsError: 548 except OSError:
588 pass # Bad path - return unchanged. 549 pass # Bad path - return unchanged.
589 elif isinstance(path, bytes): 550 elif isinstance(path, bytes):
590 path = os.getcwdb() 551 path = os.getcwdb()
591 else: 552 else:
592 path = os.getcwd() 553 path = os.getcwd()
593 return normpath(path) 554 return normpath(path)
594 555
595 # realpath is a no-op on systems without islink support 556 # realpath is a no-op on systems without islink support
596 realpath = abspath 557 realpath = abspath
597 # Win9x family and earlier have no Unicode filename support. 558 # Win9x family and earlier have no Unicode filename support.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
643 # 604 #
644 # However, the returned path will have the standard '\' separator (even if the 605 # However, the returned path will have the standard '\' separator (even if the
645 # given paths had the alternative '/' separator) and will have the case of the 606 # given paths had the alternative '/' separator) and will have the case of the
646 # first path given in the sequence. Additionally, any trailing separator is 607 # first path given in the sequence. Additionally, any trailing separator is
647 # stripped from the returned path. 608 # stripped from the returned path.
648 609
649 def commonpath(paths): 610 def commonpath(paths):
650 """Given a sequence of path names, returns the longest common sub-path.""" 611 """Given a sequence of path names, returns the longest common sub-path."""
651 612
652 if not paths: 613 if not paths:
653 return None 614 raise ValueError('commonpath() arg is an empty sequence')
654 615
655 if any(isabs(p) for p in paths) and any(not isabs(p) for p in paths): 616 if isinstance(paths[0], bytes):
656 # There is a mix of absolute and relative pathnames. 617 sep = b'\\'
657 return None 618 altsep = b'/'
658 619 curdir = b'.'
659 sep, altsep = _get_sep(paths[0]), _get_altsep(paths[0]) 620 else:
660 drivesplits = [splitdrive(normcase(p)) for p in paths] 621 sep = '\\'
622 altsep = '/'
623 curdir = '.'
624
625 drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
661 626
662 try: 627 try:
663 split_paths = [p.split(sep) for d, p in drivesplits] 628 split_paths = [p.split(sep) for d, p in drivesplits]
664 except TypeError: 629 except TypeError:
665 valid_types = all(isinstance(p, (str, bytes, bytearray)) 630 valid_types = all(isinstance(p, (str, bytes)) for p in paths)
666 for p in paths)
667 if valid_types: 631 if valid_types:
668 # Must have a mixture of text and binary data. 632 # Must have a mixture of text and binary data.
669 raise TypeError("Can't mix strings and bytes in path" 633 raise TypeError("Can't mix strings and bytes in paths") from None
670 "components.") from None
671 raise 634 raise
635
636 if len(set(p[:1] == sep for d, p in drivesplits)) != 1:
637 raise ValueError("Can't mix absolute and relative paths")
672 638
673 # Check that all drive letters or UNC paths match. The check is made only 639 # Check that all drive letters or UNC paths match. The check is made only
674 # now otherwise type errors for mixing strings and bytes would not be 640 # now otherwise type errors for mixing strings and bytes would not be
675 # caught. 641 # caught.
676 first_drive = drivesplits[0][0] 642 if len(set(d for d, p in drivesplits)) != 1:
677 if not all(d == first_drive for d, p in drivesplits): 643 raise ValueError("Paths don't have the same drive")
678 return None
679 644
680 drive, path = splitdrive(paths[0].replace(altsep, sep)) 645 drive, path = splitdrive(paths[0].replace(altsep, sep))
681 common = path.split(sep) 646 common = path.split(sep)
682 prefix = drive + sep if isabs(paths[0]) else '' 647 common = [c for c in common if c and c != curdir]
683 648
649 split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
684 s1 = min(split_paths) 650 s1 = min(split_paths)
685 s2 = max(split_paths) 651 s2 = max(split_paths)
686 for i, c in enumerate(s1): 652 for i, c in enumerate(s1):
687 if c != s2[i]: 653 if c != s2[i]:
688 common = common[:i] 654 common = common[:i]
689 break 655 break
690 else: 656 else:
691 common = common[:len(s1)] 657 common = common[:len(s1)]
692 658
659 prefix = drive + sep if isabs(paths[0]) else drive
693 if not common: 660 if not common:
694 return prefix 661 return prefix
695 else: 662 else:
696 return prefix + join(*common).rstrip(sep) 663 return prefix + sep.join(common)
697 664
698 665
699 # determine if two files are in fact the same file 666 # determine if two files are in fact the same file
700 try: 667 try:
701 # GetFinalPathNameByHandle is available starting with Windows 6.0. 668 # GetFinalPathNameByHandle is available starting with Windows 6.0.
702 # Windows XP and non-Windows OS'es will mock _getfinalpathname. 669 # Windows XP and non-Windows OS'es will mock _getfinalpathname.
703 if sys.getwindowsversion()[:2] >= (6, 0): 670 if sys.getwindowsversion()[:2] >= (6, 0):
704 from nt import _getfinalpathname 671 from nt import _getfinalpathname
705 else: 672 else:
706 raise ImportError 673 raise ImportError
707 except (AttributeError, ImportError): 674 except (AttributeError, ImportError):
708 # On Windows XP and earlier, two files are the same if their absolute 675 # On Windows XP and earlier, two files are the same if their absolute
709 # pathnames are the same. 676 # pathnames are the same.
710 # Non-Windows operating systems fake this method with an XP 677 # Non-Windows operating systems fake this method with an XP
711 # approximation. 678 # approximation.
712 def _getfinalpathname(f): 679 def _getfinalpathname(f):
713 return normcase(abspath(f)) 680 return normcase(abspath(f))
714
715 def samefile(f1, f2):
716 "Test whether two pathnames reference the same actual file"
717 return _getfinalpathname(f1) == _getfinalpathname(f2)
718
719
720 try:
721 from nt import _getfileinformation
722 except ImportError:
723 # On other operating systems, just return the fd and see that
724 # it compares equal in sameopenfile.
725 def _getfileinformation(fd):
726 return fd
727
728 def sameopenfile(f1, f2):
729 """Test whether two file objects reference the same file"""
730 return _getfileinformation(f1) == _getfileinformation(f2)
731 681
732 682
733 try: 683 try:
734 # The genericpath.isdir implementation uses os.stat and checks the mode 684 # The genericpath.isdir implementation uses os.stat and checks the mode
735 # attribute to tell whether or not the path is a directory. 685 # attribute to tell whether or not the path is a directory.
736 # This is overkill on Windows - just pass the path to GetFileAttributes 686 # This is overkill on Windows - just pass the path to GetFileAttributes
737 # and check the attribute from there. 687 # and check the attribute from there.
738 from nt import _isdir as isdir 688 from nt import _isdir as isdir
739 except ImportError: 689 except ImportError:
740 # Use genericpath.isdir as imported above. 690 # Use genericpath.isdir as imported above.
741 pass 691 pass
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+