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

Side by Side Diff: Lib/os.py

Issue 25994: File descriptor leaks in os.scandir()
Patch Set: Created 3 years, 7 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)
441
440 def is_dir(self): 442 def is_dir(self):
441 return path.isdir(self.path) 443 return path.isdir(self.path)
444
442 def is_symlink(self): 445 def is_symlink(self):
443 return path.islink(self.path) 446 return path.islink(self.path)
444 447
445 def _dummy_scandir(dir): 448 class _dummy_scandir:
446 # listdir-based implementation for bytes patches on Windows 449 # listdir-based implementation for bytes patches on Windows
447 for name in listdir(dir): 450 def __init__(self, dir):
448 yield _DummyDirEntry(dir, name) 451 self.dir = dir
452 self.it = iter(listdir(dir))
453
454 def __iter__(self):
455 return self
456
457 def __next__(self):
458 return _DummyDirEntry(self.dir, next(self.it))
459
460 def __enter__(self):
461 return self
462
463 def __exit__(self, *args):
464 self.it = iter(())
449 465
450 __all__.append("walk") 466 __all__.append("walk")
451 467
452 if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: 468 if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
453 469
454 def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir _fd=None): 470 def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir _fd=None):
455 """Directory tree generator. 471 """Directory tree generator.
456 472
457 This behaves exactly like walk(), except that it yields a 4-tuple 473 This behaves exactly like walk(), except that it yields a 4-tuple
458 474
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after
1038 return getattr(self._stream, name) 1054 return getattr(self._stream, name)
1039 def __iter__(self): 1055 def __iter__(self):
1040 return iter(self._stream) 1056 return iter(self._stream)
1041 1057
1042 # Supply os.fdopen() 1058 # Supply os.fdopen()
1043 def fdopen(fd, *args, **kwargs): 1059 def fdopen(fd, *args, **kwargs):
1044 if not isinstance(fd, int): 1060 if not isinstance(fd, int):
1045 raise TypeError("invalid fd type (%s, expected integer)" % type(fd)) 1061 raise TypeError("invalid fd type (%s, expected integer)" % type(fd))
1046 import io 1062 import io
1047 return io.open(fd, *args, **kwargs) 1063 return io.open(fd, *args, **kwargs)
OLDNEW

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