| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| 1 r"""OS routines for Mac, NT, or Posix depending on what system we're on. | 1 r"""OS routines for Mac, NT, or Posix depending on what system we're on. |
| 2 | 2 |
| 3 This exports: | 3 This exports: |
| 4 - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc. | 4 - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc. |
| 5 - os.path is either posixpath or ntpath | 5 - os.path is either posixpath or ntpath |
| 6 - os.name is either 'posix', 'nt', 'os2' or 'ce'. | 6 - os.name is either 'posix', 'nt', 'os2' or 'ce'. |
| 7 - os.curdir is a string representing the current directory ('.' or ':') | 7 - os.curdir is a string representing the current directory ('.' or ':') |
| 8 - os.pardir is a string representing the parent directory ('..' or '::') | 8 - os.pardir is a string representing the parent directory ('..' or '::') |
| 9 - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') | 9 - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') |
| 10 - os.extsep is the extension separator (always '.') | 10 - os.extsep is the extension separator (always '.') |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 324 rename(old, new) | 324 rename(old, new) |
| 325 head, tail = path.split(old) | 325 head, tail = path.split(old) |
| 326 if head and tail: | 326 if head and tail: |
| 327 try: | 327 try: |
| 328 removedirs(head) | 328 removedirs(head) |
| 329 except error: | 329 except error: |
| 330 pass | 330 pass |
| 331 | 331 |
| 332 __all__.extend(["makedirs", "removedirs", "renames"]) | 332 __all__.extend(["makedirs", "removedirs", "renames"]) |
| 333 | 333 |
| 334 def walk(top, topdown=True, onerror=None, followlinks=False): | 334 def _isdir_dir_fd(path, dir_fd): |
| 335 """ | |
| 336 A substitute for os.path.isdir that supports dir_fd. | |
| 337 """ | |
| 338 try: | |
| 339 return st.S_ISDIR(stat(path, dir_fd=dir_fd).st_mode) | |
| 340 except FileNotFoundError: | |
| 341 return False | |
|
storchaka
2012/06/25 14:08:10
Should onerror handler be used?
larry
2012/06/25 15:04:13
I have a much better implementation, so this one i
| |
| 342 | |
| 343 def walk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd=None) : | |
| 335 """Directory tree generator. | 344 """Directory tree generator. |
| 336 | 345 |
| 337 For each directory in the directory tree rooted at top (including top | 346 For each directory in the directory tree rooted at top (including top |
| 338 itself, but excluding '.' and '..'), yields a 3-tuple | 347 itself, but excluding '.' and '..'), yields a 3-tuple |
| 339 | 348 |
| 340 dirpath, dirnames, filenames | 349 dirpath, dirnames, filenames |
| 341 | 350 |
| 342 dirpath is a string, the path to the directory. dirnames is a list of | 351 dirpath is a string, the path to the directory. dirnames is a list of |
| 343 the names of the subdirectories in dirpath (excluding '.' and '..'). | 352 the names of the subdirectories in dirpath (excluding '.' and '..'). |
| 344 filenames is a list of the names of the non-directory files in dirpath. | 353 filenames is a list of the names of the non-directory files in dirpath. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 388 dirs.remove('CVS') # don't visit CVS directories | 397 dirs.remove('CVS') # don't visit CVS directories |
| 389 """ | 398 """ |
| 390 | 399 |
| 391 islink, join, isdir = path.islink, path.join, path.isdir | 400 islink, join, isdir = path.islink, path.join, path.isdir |
| 392 | 401 |
| 393 # We may not have read permission for top, in which case we can't | 402 # We may not have read permission for top, in which case we can't |
| 394 # get a list of the files the directory contains. os.walk | 403 # get a list of the files the directory contains. os.walk |
| 395 # always suppressed the exception then, rather than blow up for a | 404 # always suppressed the exception then, rather than blow up for a |
| 396 # minor reason when (say) a thousand readable directories are still | 405 # minor reason when (say) a thousand readable directories are still |
| 397 # left to visit. That logic is copied here. | 406 # left to visit. That logic is copied here. |
| 407 # | |
| 408 # Note that listdir and error are globals in this module due | |
| 409 # to earlier import-*. | |
| 398 try: | 410 try: |
| 399 # Note that listdir and error are globals in this module due | 411 if dir_fd is None: |
| 400 # to earlier import-*. | 412 close_me = None |
| 401 names = listdir(top) | 413 names = listdir(top) |
| 414 else: | |
| 415 close_me = open(top, O_RDONLY, dir_fd=dir_fd) | |
| 416 names = listdir(close_me) | |
| 402 except error as err: | 417 except error as err: |
| 418 if close_me is not None: | |
| 419 close(close_me) | |
|
storchaka
2012/06/25 14:08:10
What if error happens on close?
larry
2012/06/25 15:04:13
Obviated by new implementation.
| |
| 403 if onerror is not None: | 420 if onerror is not None: |
| 404 onerror(err) | 421 onerror(err) |
| 405 return | 422 return |
| 406 | 423 |
| 407 dirs, nondirs = [], [] | 424 dirs, nondirs = [], [] |
| 408 for name in names: | 425 if close_me is not None: |
| 409 if isdir(join(top, name)): | 426 for name in names: |
| 410 dirs.append(name) | 427 # name is in directory referenced by close_me! |
| 411 else: | 428 # don't bother joining with top here. |
| 412 nondirs.append(name) | 429 if _isdir_dir_fd(name, close_me): |
| 430 dirs.append(name) | |
| 431 else: | |
| 432 nondirs.append(name) | |
| 433 close(close_me) | |
| 434 else: | |
| 435 for name in names: | |
| 436 new_path = join(top, name) | |
| 437 if isdir(new_path): | |
| 438 dirs.append(name) | |
| 439 else: | |
| 440 nondirs.append(name) | |
| 413 | 441 |
| 414 if topdown: | 442 if topdown: |
| 415 yield top, dirs, nondirs | 443 yield top, dirs, nondirs |
| 416 for name in dirs: | 444 for name in dirs: |
| 417 new_path = join(top, name) | 445 new_path = join(top, name) |
| 418 if followlinks or not islink(new_path): | 446 if followlinks or not islink(new_path): |
|
storchaka
2012/06/25 14:08:10
st.S_ISLINK(stat(name, dir_fd=close_me).st_mode) ?
larry
2012/06/25 15:04:13
Obviated by new implementation.
| |
| 419 yield from walk(new_path, topdown, onerror, followlinks) | 447 yield from walk(new_path, topdown, onerror, followlinks, dir_fd=dir_ fd) |
| 420 if not topdown: | 448 if not topdown: |
| 421 yield top, dirs, nondirs | 449 yield top, dirs, nondirs |
| 422 | 450 |
| 423 __all__.append("walk") | 451 __all__.append("walk") |
| 452 if (listdir in supports_fd) and ({open, stat} <= supports_dir_fd): | |
| 453 supports_dir_fd.add(walk) | |
| 424 | 454 |
| 425 if open in supports_dir_fd: | 455 if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: |
| 426 | 456 |
| 427 def fwalk(top, topdown=True, onerror=None, followlinks=False): | 457 def fwalk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd= None): |
| 428 """Directory tree generator. | 458 """Directory tree generator. |
| 429 | 459 |
| 430 This behaves exactly like walk(), except that it yields a 4-tuple | 460 This behaves exactly like walk(), except that it yields a 4-tuple |
| 431 | 461 |
| 432 dirpath, dirnames, filenames, dirfd | 462 dirpath, dirnames, filenames, dirfd |
| 433 | 463 |
| 434 `dirpath`, `dirnames` and `filenames` are identical to walk() output, | 464 `dirpath`, `dirnames` and `filenames` are identical to walk() output, |
| 435 and `dirfd` is a file descriptor referring to the directory `dirpath`. | 465 and `dirfd` is a file descriptor referring to the directory `dirpath`. |
| 436 | 466 |
| 437 The advantage of walkfd() over walk() is that it's safe against symlink | 467 The advantage of fwalk() over walk() is that it's safe against symlink |
| 438 races (when followlinks is False). | 468 races (when followlinks is False). |
| 469 | |
| 470 If dir_fd is not None, it should be a file descriptor open to a director y, | |
| 471 and top should be relative; top will then be relative to that director y. | |
| 472 (dir_fd is always supported for fwalk.) | |
| 439 | 473 |
| 440 Caution: | 474 Caution: |
| 441 Since fwalk() yields file descriptors, those are only valid until the | 475 Since fwalk() yields file descriptors, those are only valid until the |
| 442 next iteration step, so you should dup() them if you want to keep them | 476 next iteration step, so you should dup() them if you want to keep them |
| 443 for a longer period. | 477 for a longer period. |
| 444 | 478 |
| 445 Example: | 479 Example: |
| 446 | 480 |
| 447 import os | 481 import os |
| 448 for root, dirs, files, rootfd in os.fwalk('python/Lib/email'): | 482 for root, dirs, files, rootfd in os.fwalk('python/Lib/email'): |
| 449 print(root, "consumes", end="") | 483 print(root, "consumes", end="") |
| 450 print(sum([os.stat(name, dir_fd=rootfd).st_size for name in files]), | 484 print(sum([os.stat(name, dir_fd=rootfd).st_size for name in files]), |
| 451 end="") | 485 end="") |
| 452 print("bytes in", len(files), "non-directory files") | 486 print("bytes in", len(files), "non-directory files") |
| 453 if 'CVS' in dirs: | 487 if 'CVS' in dirs: |
| 454 dirs.remove('CVS') # don't visit CVS directories | 488 dirs.remove('CVS') # don't visit CVS directories |
| 455 """ | 489 """ |
| 456 # Note: To guard against symlink races, we use the standard | 490 # Note: To guard against symlink races, we use the standard |
| 457 # lstat()/open()/fstat() trick. | 491 # lstat()/open()/fstat() trick. |
| 458 orig_st = lstat(top) | 492 orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd) |
| 459 topfd = open(top, O_RDONLY) | 493 topfd = open(top, O_RDONLY, dir_fd=dir_fd) |
| 460 try: | 494 try: |
| 461 if (followlinks or (st.S_ISDIR(orig_st.st_mode) and | 495 if (followlinks or (st.S_ISDIR(orig_st.st_mode) and |
| 462 path.samestat(orig_st, fstat(topfd)))): | 496 path.samestat(orig_st, stat(topfd)))): |
| 463 yield from _fwalk(topfd, top, topdown, onerror, followlinks) | 497 yield from _fwalk(topfd, top, topdown, onerror, followlinks) |
| 464 finally: | 498 finally: |
| 465 close(topfd) | 499 close(topfd) |
| 466 | 500 |
| 467 def _fwalk(topfd, toppath, topdown, onerror, followlinks): | 501 def _fwalk(topfd, toppath, topdown, onerror, followlinks): |
| 468 # Note: This uses O(depth of the directory tree) file descriptors: if | 502 # Note: This uses O(depth of the directory tree) file descriptors: if |
| 469 # necessary, it can be adapted to only require O(1) FDs, see issue | 503 # necessary, it can be adapted to only require O(1) FDs, see issue |
| 470 # #13734. | 504 # #13734. |
| 471 | 505 |
| 472 names = listdir(topfd) | 506 names = listdir(topfd) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 495 | 529 |
| 496 for name in dirs: | 530 for name in dirs: |
| 497 try: | 531 try: |
| 498 orig_st = stat(name, dir_fd=topfd, follow_symlinks=followlinks) | 532 orig_st = stat(name, dir_fd=topfd, follow_symlinks=followlinks) |
| 499 dirfd = open(name, O_RDONLY, dir_fd=topfd) | 533 dirfd = open(name, O_RDONLY, dir_fd=topfd) |
| 500 except error as err: | 534 except error as err: |
| 501 if onerror is not None: | 535 if onerror is not None: |
| 502 onerror(err) | 536 onerror(err) |
| 503 return | 537 return |
| 504 try: | 538 try: |
| 505 if followlinks or path.samestat(orig_st, fstat(dirfd)): | 539 if followlinks or path.samestat(orig_st, stat(dirfd)): |
| 506 dirpath = path.join(toppath, name) | 540 dirpath = path.join(toppath, name) |
| 507 yield from _fwalk(dirfd, dirpath, topdown, onerror, followli nks) | 541 yield from _fwalk(dirfd, dirpath, topdown, onerror, followli nks) |
| 508 finally: | 542 finally: |
| 509 close(dirfd) | 543 close(dirfd) |
| 510 | 544 |
| 511 if not topdown: | 545 if not topdown: |
| 512 yield toppath, dirs, nondirs, topfd | 546 yield toppath, dirs, nondirs, topfd |
| 513 | 547 |
| 514 __all__.append("fwalk") | 548 __all__.append("fwalk") |
| 515 | 549 |
| (...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1019 return getattr(self._stream, name) | 1053 return getattr(self._stream, name) |
| 1020 def __iter__(self): | 1054 def __iter__(self): |
| 1021 return iter(self._stream) | 1055 return iter(self._stream) |
| 1022 | 1056 |
| 1023 # Supply os.fdopen() | 1057 # Supply os.fdopen() |
| 1024 def fdopen(fd, *args, **kwargs): | 1058 def fdopen(fd, *args, **kwargs): |
| 1025 if not isinstance(fd, int): | 1059 if not isinstance(fd, int): |
| 1026 raise TypeError("invalid fd type (%s, expected integer)" % type(fd)) | 1060 raise TypeError("invalid fd type (%s, expected integer)" % type(fd)) |
| 1027 import io | 1061 import io |
| 1028 return io.open(fd, *args, **kwargs) | 1062 return io.open(fd, *args, **kwargs) |
| OLD | NEW |