@@ -133,6 +133,27 @@ def _sanitize_params(prefix, suffix, dir):
133
133
return prefix , suffix , dir , output_type
134
134
135
135
136
+ def _infer_return_type (* args ):
137
+ """Look at the type of all args and divine their implied return type."""
138
+ return_type = None
139
+ for arg in args :
140
+ if arg is None :
141
+ continue
142
+ if isinstance (arg , bytes ):
143
+ if return_type is str :
144
+ raise TypeError ("Can't mix bytes and non-bytes in "
145
+ "path components." )
146
+ return_type = bytes
147
+ else :
148
+ if return_type is bytes :
149
+ raise TypeError ("Can't mix bytes and non-bytes in "
150
+ "path components." )
151
+ return_type = str
152
+ if return_type is None :
153
+ return str # tempfile APIs return a str by default.
154
+ return return_type
155
+
156
+
136
157
class _RandomNameSequence :
137
158
"""An instance of _RandomNameSequence generates an endless
138
159
sequence of unpredictable strings which can safely be incorporated
@@ -275,6 +296,22 @@ def _mkstemp_inner(dir, pre, suf, flags, output_type):
275
296
raise FileExistsError (_errno .EEXIST ,
276
297
"No usable temporary file name found" )
277
298
299
+ def _dont_follow_symlinks (func , path , * args ):
300
+ # Pass follow_symlinks=False, unless not supported on this platform.
301
+ if func in _os .supports_follow_symlinks :
302
+ func (path , * args , follow_symlinks = False )
303
+ elif _os .name == 'nt' or not _os .path .islink (path ):
304
+ func (path , * args )
305
+
306
+ def _resetperms (path ):
307
+ try :
308
+ chflags = _os .chflags
309
+ except AttributeError :
310
+ pass
311
+ else :
312
+ _dont_follow_symlinks (chflags , path , 0 )
313
+ _dont_follow_symlinks (_os .chmod , path , 0o700 )
314
+
278
315
279
316
# User visible interfaces.
280
317
@@ -776,7 +813,7 @@ def writelines(self, iterable):
776
813
return rv
777
814
778
815
779
- class TemporaryDirectory ( object ) :
816
+ class TemporaryDirectory :
780
817
"""Create and return a temporary directory. This has the same
781
818
behavior as mkdtemp but can be used as a context manager. For
782
819
example:
@@ -785,19 +822,75 @@ class TemporaryDirectory(object):
785
822
...
786
823
787
824
Upon exiting the context, the directory and everything contained
788
- in it are removed.
825
+ in it are removed (unless delete=False is passed or an exception
826
+ is raised during cleanup and ignore_cleanup_errors is not True).
827
+
828
+ Optional Arguments:
829
+ suffix - A str suffix for the directory name. (see mkdtemp)
830
+ prefix - A str prefix for the directory name. (see mkdtemp)
831
+ dir - A directory to create this temp dir in. (see mkdtemp)
832
+ ignore_cleanup_errors - False; ignore exceptions during cleanup?
833
+ delete - True; whether the directory is automatically deleted.
789
834
"""
790
835
791
- def __init__ (self , suffix = None , prefix = None , dir = None ):
836
+ def __init__ (self , suffix = None , prefix = None , dir = None ,
837
+ ignore_cleanup_errors = False , * , delete = True ):
792
838
self .name = mkdtemp (suffix , prefix , dir )
839
+ self ._ignore_cleanup_errors = ignore_cleanup_errors
840
+ self ._delete = delete
793
841
self ._finalizer = _weakref .finalize (
794
842
self , self ._cleanup , self .name ,
795
- warn_message = "Implicitly cleaning up {!r}" .format (self ))
843
+ warn_message = "Implicitly cleaning up {!r}" .format (self ),
844
+ ignore_errors = self ._ignore_cleanup_errors , delete = self ._delete )
845
+
846
+ @classmethod
847
+ def _rmtree (cls , name , ignore_errors = False , repeated = False ):
848
+ def onexc (func , path , exc_info ):
849
+ exc = exc_info [1 ]
850
+ if isinstance (exc , PermissionError ):
851
+ if repeated and path == name :
852
+ if ignore_errors :
853
+ return
854
+ raise
855
+
856
+ try :
857
+ if path != name :
858
+ _resetperms (_os .path .dirname (path ))
859
+ _resetperms (path )
860
+
861
+ try :
862
+ _os .unlink (path )
863
+ except IsADirectoryError :
864
+ cls ._rmtree (path )
865
+ except PermissionError :
866
+ # The PermissionError handler was originally added for
867
+ # FreeBSD in directories, but it seems that it is raised
868
+ # on Windows too.
869
+ # bpo-43153: Calling _rmtree again may
870
+ # raise NotADirectoryError and mask the PermissionError.
871
+ # So we must re-raise the current PermissionError if
872
+ # path is not a directory.
873
+ if not _os .path .isdir (path ) or _os .path .isjunction (path ):
874
+ if ignore_errors :
875
+ return
876
+ raise
877
+ cls ._rmtree (path , ignore_errors = ignore_errors ,
878
+ repeated = (path == name ))
879
+ except FileNotFoundError :
880
+ pass
881
+ elif isinstance (exc , FileNotFoundError ):
882
+ pass
883
+ else :
884
+ if not ignore_errors :
885
+ raise
886
+
887
+ _shutil .rmtree (name , onerror = onexc )
796
888
797
889
@classmethod
798
- def _cleanup (cls , name , warn_message ):
799
- _shutil .rmtree (name )
800
- _warnings .warn (warn_message , ResourceWarning )
890
+ def _cleanup (cls , name , warn_message , ignore_errors = False , delete = True ):
891
+ if delete :
892
+ cls ._rmtree (name , ignore_errors = ignore_errors )
893
+ _warnings .warn (warn_message , ResourceWarning )
801
894
802
895
def __repr__ (self ):
803
896
return "<{} {!r}>" .format (self .__class__ .__name__ , self .name )
@@ -806,8 +899,9 @@ def __enter__(self):
806
899
return self .name
807
900
808
901
def __exit__ (self , exc , value , tb ):
809
- self .cleanup ()
902
+ if self ._delete :
903
+ self .cleanup ()
810
904
811
905
def cleanup (self ):
812
- if self ._finalizer .detach ():
813
- _shutil . rmtree (self .name )
906
+ if self ._finalizer .detach () or _os . path . exists ( self . name ) :
907
+ self . _rmtree (self .name , ignore_errors = self . _ignore_cleanup_errors )
0 commit comments