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

Side by Side Diff: Lib/os.py

Issue 25994: File descriptor leaks in os.scandir()
Patch Set: Created 3 years, 8 months 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:
View unified diff | Download patch
OLDNEW
1 r"""OS routines for NT or Posix depending on what system we're on. 1 r"""OS routines for NT or Posix depending on what system we're on.
2 2
3 This exports: 3 This exports:
4 - all functions from posix, nt or ce, e.g. unlink, stat, etc. 4 - all functions from posix, nt 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' or 'ce'. 6 - os.name is either 'posix', 'nt' 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 356 matching lines...) Expand 10 before | Expand all | Expand 10 after
367 scandir_it = _dummy_scandir(top) 367 scandir_it = _dummy_scandir(top)
368 else: 368 else:
369 # Note that scandir is global in this module due 369 # Note that scandir is global in this module due
370 # to earlier import-*. 370 # to earlier import-*.
371 scandir_it = scandir(top) 371 scandir_it = scandir(top)
372 except OSError as error: 372 except OSError as error:
373 if onerror is not None: 373 if onerror is not None:
374 onerror(error) 374 onerror(error)
375 return 375 return
376 376
377 while True: 377 with scandir_it:
378 try: 378 while True:
379 try: 379 try:
380 entry = next(scandir_it) 380 try:
381 except StopIteration: 381 entry = next(scandir_it)
382 break 382 except StopIteration:
383 except OSError as error: 383 break
384 if onerror is not None: 384 except OSError as error:
385 onerror(error) 385 if onerror is not None:
386 return 386 onerror(error)
387 return
387 388
388 try: 389 try:
389 is_dir = entry.is_dir() 390 is_dir = entry.is_dir()
390 except OSError: 391 except OSError:
391 # If is_dir() raises an OSError, consider that the entry is not 392 # If is_dir() raises an OSError, consider that the entry is not
392 # a directory, same behaviour than os.path.isdir(). 393 # a directory, same behaviour than os.path.isdir().
393 is_dir = False 394 is_dir = False
394 395
395 if is_dir: 396 if is_dir:
396 dirs.append(entry.name) 397 dirs.append(entry.name)
397 else: 398 else:
398 nondirs.append(entry.name) 399 nondirs.append(entry.name)
399 400
400 if not topdown and is_dir: 401 if not topdown and is_dir:
401 # Bottom-up: recurse into sub-directory, but exclude symlinks to 402 # Bottom-up: recurse into sub-directory, but exclude symlinks to
402 # directories if followlinks is False 403 # directories if followlinks is False
403 if followlinks: 404 if followlinks:
404 walk_into = True 405 walk_into = True
405 else: 406 else:
406 try: 407 try:
407 is_symlink = entry.is_symlink() 408 is_symlink = entry.is_symlink()
408 except OSError: 409 except OSError:
409 # If is_symlink() raises an OSError, consider that the 410 # If is_symlink() raises an OSError, consider that the
410 # entry is not a symbolic link, same behaviour than 411 # entry is not a symbolic link, same behaviour than
411 # os.path.islink(). 412 # os.path.islink().
412 is_symlink = False 413 is_symlink = False
413 walk_into = not is_symlink 414 walk_into = not is_symlink
414 415
415 if walk_into: 416 if walk_into:
416 yield from walk(entry.path, topdown, onerror, followlinks) 417 yield from walk(entry.path, topdown, onerror, followlinks)
417 418
418 # Yield before recursion if going top down 419 # Yield before recursion if going top down
419 if topdown: 420 if topdown:
420 yield top, dirs, nondirs 421 yield top, dirs, nondirs
421 422
422 # Recurse into sub-directories 423 # Recurse into sub-directories
423 islink, join = path.islink, path.join 424 islink, join = path.islink, path.join
424 for dirname in dirs: 425 for dirname in dirs:
425 new_path = join(top, dirname) 426 new_path = join(top, dirname)
426 # Issue #23605: os.path.islink() is used instead of caching 427 # Issue #23605: os.path.islink() is used instead of caching
427 # entry.is_symlink() result during the loop on os.scandir() because 428 # entry.is_symlink() result during the loop on os.scandir() because
428 # the caller can replace the directory entry during the "yield" 429 # the caller can replace the directory entry during the "yield"
429 # above. 430 # above.
430 if followlinks or not islink(new_path): 431 if followlinks or not islink(new_path):
431 yield from walk(new_path, topdown, onerror, followlinks) 432 yield from walk(new_path, topdown, onerror, followlinks)
432 else: 433 else:
433 # Yield after recursion if going bottom up 434 # Yield after recursion if going bottom up
434 yield top, dirs, nondirs 435 yield top, dirs, nondirs
435 436
436 class _DummyDirEntry: 437 class _DummyDirEntry:
437 def __init__(self, dir, name): 438 def __init__(self, dir, name):
438 self.name = name 439 self.name = name
439 self.path = path.join(dir, name) 440 self.path = path.join(dir, name)
440 def is_dir(self): 441 def is_dir(self):
441 return path.isdir(self.path) 442 return path.isdir(self.path)
442 def is_symlink(self): 443 def is_symlink(self):
443 return path.islink(self.path) 444 return path.islink(self.path)
444 445
445 def _dummy_scandir(dir): 446 class _dummy_scandir:
446 # listdir-based implementation for bytes patches on Windows 447 # listdir-based implementation for bytes patches on Windows
447 for name in listdir(dir): 448 def __init__(self, dir):
448 yield _DummyDirEntry(dir, name) 449 self.dir = dir
450 self.it = iter(listdir(dir))
451 def __iter__(self):
452 return self
453 def __next__(self):
454 return _DummyDirEntry(self.dir, next(self.it))
455 def __enter__(self):
456 return self
457 def __exit__(self, *args):
458 self.it = iter(())
449 459
450 __all__.append("walk") 460 __all__.append("walk")
451 461
452 if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: 462 if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
453 463
454 def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir _fd=None): 464 def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir _fd=None):
455 """Directory tree generator. 465 """Directory tree generator.
456 466
457 This behaves exactly like walk(), except that it yields a 4-tuple 467 This behaves exactly like walk(), except that it yields a 4-tuple
458 468
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after
1038 return getattr(self._stream, name) 1048 return getattr(self._stream, name)
1039 def __iter__(self): 1049 def __iter__(self):
1040 return iter(self._stream) 1050 return iter(self._stream)
1041 1051
1042 # Supply os.fdopen() 1052 # Supply os.fdopen()
1043 def fdopen(fd, *args, **kwargs): 1053 def fdopen(fd, *args, **kwargs):
1044 if not isinstance(fd, int): 1054 if not isinstance(fd, int):
1045 raise TypeError("invalid fd type (%s, expected integer)" % type(fd)) 1055 raise TypeError("invalid fd type (%s, expected integer)" % type(fd))
1046 import io 1056 import io
1047 return io.open(fd, *args, **kwargs) 1057 return io.open(fd, *args, **kwargs)
OLDNEW

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