@@ -575,12 +575,12 @@ def _rmtree_islink(path):
575
575
return os .path .islink (path )
576
576
577
577
# version vulnerable to race conditions
578
- def _rmtree_unsafe (path , onerror ):
578
+ def _rmtree_unsafe (path , onexc ):
579
579
try :
580
580
with os .scandir (path ) as scandir_it :
581
581
entries = list (scandir_it )
582
- except OSError :
583
- onerror (os .scandir , path , sys . exc_info () )
582
+ except OSError as err :
583
+ onexc (os .scandir , path , err )
584
584
entries = []
585
585
for entry in entries :
586
586
fullname = entry .path
@@ -596,28 +596,28 @@ def _rmtree_unsafe(path, onerror):
596
596
# a directory with a symlink after the call to
597
597
# os.scandir or entry.is_dir above.
598
598
raise OSError ("Cannot call rmtree on a symbolic link" )
599
- except OSError :
600
- onerror (os .path .islink , fullname , sys . exc_info () )
599
+ except OSError as err :
600
+ onexc (os .path .islink , fullname , err )
601
601
continue
602
- _rmtree_unsafe (fullname , onerror )
602
+ _rmtree_unsafe (fullname , onexc )
603
603
else :
604
604
try :
605
605
os .unlink (fullname )
606
- except OSError :
607
- onerror (os .unlink , fullname , sys . exc_info () )
606
+ except OSError as err :
607
+ onexc (os .unlink , fullname , err )
608
608
try :
609
609
os .rmdir (path )
610
- except OSError :
611
- onerror (os .rmdir , path , sys . exc_info () )
610
+ except OSError as err :
611
+ onexc (os .rmdir , path , err )
612
612
613
613
# Version using fd-based APIs to protect against races
614
- def _rmtree_safe_fd (topfd , path , onerror ):
614
+ def _rmtree_safe_fd (topfd , path , onexc ):
615
615
try :
616
616
with os .scandir (topfd ) as scandir_it :
617
617
entries = list (scandir_it )
618
618
except OSError as err :
619
619
err .filename = path
620
- onerror (os .scandir , path , sys . exc_info () )
620
+ onexc (os .scandir , path , err )
621
621
return
622
622
for entry in entries :
623
623
fullname = os .path .join (path , entry .name )
@@ -630,71 +630,89 @@ def _rmtree_safe_fd(topfd, path, onerror):
630
630
try :
631
631
orig_st = entry .stat (follow_symlinks = False )
632
632
is_dir = stat .S_ISDIR (orig_st .st_mode )
633
- except OSError :
634
- onerror (os .lstat , fullname , sys . exc_info () )
633
+ except OSError as err :
634
+ onexc (os .lstat , fullname , err )
635
635
continue
636
636
if is_dir :
637
637
try :
638
638
dirfd = os .open (entry .name , os .O_RDONLY , dir_fd = topfd )
639
639
dirfd_closed = False
640
- except OSError :
641
- onerror (os .open , fullname , sys . exc_info () )
640
+ except OSError as err :
641
+ onexc (os .open , fullname , err )
642
642
else :
643
643
try :
644
644
if os .path .samestat (orig_st , os .fstat (dirfd )):
645
- _rmtree_safe_fd (dirfd , fullname , onerror )
645
+ _rmtree_safe_fd (dirfd , fullname , onexc )
646
646
try :
647
647
os .close (dirfd )
648
648
dirfd_closed = True
649
649
os .rmdir (entry .name , dir_fd = topfd )
650
- except OSError :
651
- onerror (os .rmdir , fullname , sys . exc_info () )
650
+ except OSError as err :
651
+ onexc (os .rmdir , fullname , err )
652
652
else :
653
653
try :
654
654
# This can only happen if someone replaces
655
655
# a directory with a symlink after the call to
656
656
# os.scandir or stat.S_ISDIR above.
657
657
raise OSError ("Cannot call rmtree on a symbolic "
658
658
"link" )
659
- except OSError :
660
- onerror (os .path .islink , fullname , sys . exc_info () )
659
+ except OSError as err :
660
+ onexc (os .path .islink , fullname , err )
661
661
finally :
662
662
if not dirfd_closed :
663
663
os .close (dirfd )
664
664
else :
665
665
try :
666
666
os .unlink (entry .name , dir_fd = topfd )
667
- except OSError :
668
- onerror (os .unlink , fullname , sys . exc_info () )
667
+ except OSError as err :
668
+ onexc (os .unlink , fullname , err )
669
669
670
670
_use_fd_functions = ({os .open , os .stat , os .unlink , os .rmdir } <=
671
671
os .supports_dir_fd and
672
672
os .scandir in os .supports_fd and
673
673
os .stat in os .supports_follow_symlinks )
674
674
675
- def rmtree (path , ignore_errors = False , onerror = None , * , dir_fd = None ):
675
+ def rmtree (path , ignore_errors = False , onerror = None , * , onexc = None , dir_fd = None ):
676
676
"""Recursively delete a directory tree.
677
677
678
678
If dir_fd is not None, it should be a file descriptor open to a directory;
679
679
path will then be relative to that directory.
680
680
dir_fd may not be implemented on your platform.
681
681
If it is unavailable, using it will raise a NotImplementedError.
682
682
683
- If ignore_errors is set, errors are ignored; otherwise, if onerror
684
- is set, it is called to handle the error with arguments (func,
683
+ If ignore_errors is set, errors are ignored; otherwise, if onexc or
684
+ onerror is set, it is called to handle the error with arguments (func,
685
685
path, exc_info) where func is platform and implementation dependent;
686
686
path is the argument to that function that caused it to fail; and
687
- exc_info is a tuple returned by sys.exc_info(). If ignore_errors
688
- is false and onerror is None, an exception is raised.
687
+ the value of exc_info describes the exception. For onexc it is the
688
+ exception instance, and for onerror it is a tuple as returned by
689
+ sys.exc_info(). If ignore_errors is false and both onexc and
690
+ onerror are None, the exception is reraised.
689
691
692
+ onerror is deprecated and only remains for backwards compatibility.
693
+ If both onerror and onexc are set, onerror is ignored and onexc is used.
690
694
"""
691
695
sys .audit ("shutil.rmtree" , path , dir_fd )
692
696
if ignore_errors :
693
- def onerror (* args ):
697
+ def onexc (* args ):
694
698
pass
695
- elif onerror is None :
696
- def onerror (* args ):
699
+ elif onerror is None and onexc is None :
700
+ def onexc (* args ):
697
701
raise
702
+ elif onexc is None :
703
+ if onerror is None :
704
+ def onexc (* args ):
705
+ raise
706
+ else :
707
+ # delegate to onerror
708
+ def onexc (* args ):
709
+ func , path , exc = args
710
+ if exc is None :
711
+ exc_info = None , None , None
712
+ else :
713
+ exc_info = type (exc ), exc , exc .__traceback__
714
+ return onerror (func , path , exc_info )
715
+
698
716
if _use_fd_functions :
699
717
# While the unsafe rmtree works fine on bytes, the fd based does not.
700
718
if isinstance (path , bytes ):
@@ -703,30 +721,30 @@ def onerror(*args):
703
721
# lstat()/open()/fstat() trick.
704
722
try :
705
723
orig_st = os .lstat (path , dir_fd = dir_fd )
706
- except Exception :
707
- onerror (os .lstat , path , sys . exc_info () )
724
+ except Exception as err :
725
+ onexc (os .lstat , path , err )
708
726
return
709
727
try :
710
728
fd = os .open (path , os .O_RDONLY , dir_fd = dir_fd )
711
729
fd_closed = False
712
- except Exception :
713
- onerror (os .open , path , sys . exc_info () )
730
+ except Exception as err :
731
+ onexc (os .open , path , err )
714
732
return
715
733
try :
716
734
if os .path .samestat (orig_st , os .fstat (fd )):
717
- _rmtree_safe_fd (fd , path , onerror )
735
+ _rmtree_safe_fd (fd , path , onexc )
718
736
try :
719
737
os .close (fd )
720
738
fd_closed = True
721
739
os .rmdir (path , dir_fd = dir_fd )
722
- except OSError :
723
- onerror (os .rmdir , path , sys . exc_info () )
740
+ except OSError as err :
741
+ onexc (os .rmdir , path , err )
724
742
else :
725
743
try :
726
744
# symlinks to directories are forbidden, see bug #1669
727
745
raise OSError ("Cannot call rmtree on a symbolic link" )
728
- except OSError :
729
- onerror (os .path .islink , path , sys . exc_info () )
746
+ except OSError as err :
747
+ onexc (os .path .islink , path , err )
730
748
finally :
731
749
if not fd_closed :
732
750
os .close (fd )
@@ -737,11 +755,11 @@ def onerror(*args):
737
755
if _rmtree_islink (path ):
738
756
# symlinks to directories are forbidden, see bug #1669
739
757
raise OSError ("Cannot call rmtree on a symbolic link" )
740
- except OSError :
741
- onerror (os .path .islink , path , sys . exc_info () )
742
- # can't continue even if onerror hook returns
758
+ except OSError as err :
759
+ onexc (os .path .islink , path , err )
760
+ # can't continue even if onexc hook returns
743
761
return
744
- return _rmtree_unsafe (path , onerror )
762
+ return _rmtree_unsafe (path , onexc )
745
763
746
764
# Allow introspection of whether or not the hardening against symlink
747
765
# attacks is supported on the current platform
0 commit comments