• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Temporary files.
2
3This module provides generic, low- and high-level interfaces for
4creating temporary files and directories.  All of the interfaces
5provided by this module can be used without fear of race conditions
6except for 'mktemp'.  'mktemp' is subject to race conditions and
7should not be used; it is provided for backward compatibility only.
8
9The default path names are returned as str.  If you supply bytes as
10input, all return values will be in bytes.  Ex:
11
12    >>> tempfile.mkstemp()
13    (4, '/tmp/tmptpu9nin8')
14    >>> tempfile.mkdtemp(suffix=b'')
15    b'/tmp/tmppbi8f0hy'
16
17This module also provides some data items to the user:
18
19  TMP_MAX  - maximum number of names that will be tried before
20             giving up.
21  tempdir  - If this is set to a string before the first use of
22             any routine from this module, it will be considered as
23             another candidate location to store temporary files.
24"""
25
26__all__ = [
27    "NamedTemporaryFile", "TemporaryFile", # high level safe interfaces
28    "SpooledTemporaryFile", "TemporaryDirectory",
29    "mkstemp", "mkdtemp",                  # low level safe interfaces
30    "mktemp",                              # deprecated unsafe interface
31    "TMP_MAX", "gettempprefix",            # constants
32    "tempdir", "gettempdir",
33    "gettempprefixb", "gettempdirb",
34   ]
35
36
37# Imports.
38
39import functools as _functools
40import warnings as _warnings
41import io as _io
42import os as _os
43import shutil as _shutil
44import errno as _errno
45from random import Random as _Random
46import sys as _sys
47import weakref as _weakref
48import _thread
49_allocate_lock = _thread.allocate_lock
50
51_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
52if hasattr(_os, 'O_NOFOLLOW'):
53    _text_openflags |= _os.O_NOFOLLOW
54
55_bin_openflags = _text_openflags
56if hasattr(_os, 'O_BINARY'):
57    _bin_openflags |= _os.O_BINARY
58
59if hasattr(_os, 'TMP_MAX'):
60    TMP_MAX = _os.TMP_MAX
61else:
62    TMP_MAX = 10000
63
64# This variable _was_ unused for legacy reasons, see issue 10354.
65# But as of 3.5 we actually use it at runtime so changing it would
66# have a possibly desirable side effect...  But we do not want to support
67# that as an API.  It is undocumented on purpose.  Do not depend on this.
68template = "tmp"
69
70# Internal routines.
71
72_once_lock = _allocate_lock()
73
74
75def _exists(fn):
76    try:
77        _os.lstat(fn)
78    except OSError:
79        return False
80    else:
81        return True
82
83
84def _infer_return_type(*args):
85    """Look at the type of all args and divine their implied return type."""
86    return_type = None
87    for arg in args:
88        if arg is None:
89            continue
90        if isinstance(arg, bytes):
91            if return_type is str:
92                raise TypeError("Can't mix bytes and non-bytes in "
93                                "path components.")
94            return_type = bytes
95        else:
96            if return_type is bytes:
97                raise TypeError("Can't mix bytes and non-bytes in "
98                                "path components.")
99            return_type = str
100    if return_type is None:
101        return str  # tempfile APIs return a str by default.
102    return return_type
103
104
105def _sanitize_params(prefix, suffix, dir):
106    """Common parameter processing for most APIs in this module."""
107    output_type = _infer_return_type(prefix, suffix, dir)
108    if suffix is None:
109        suffix = output_type()
110    if prefix is None:
111        if output_type is str:
112            prefix = template
113        else:
114            prefix = _os.fsencode(template)
115    if dir is None:
116        if output_type is str:
117            dir = gettempdir()
118        else:
119            dir = gettempdirb()
120    return prefix, suffix, dir, output_type
121
122
123class _RandomNameSequence:
124    """An instance of _RandomNameSequence generates an endless
125    sequence of unpredictable strings which can safely be incorporated
126    into file names.  Each string is eight characters long.  Multiple
127    threads can safely use the same instance at the same time.
128
129    _RandomNameSequence is an iterator."""
130
131    characters = "abcdefghijklmnopqrstuvwxyz0123456789_"
132
133    @property
134    def rng(self):
135        cur_pid = _os.getpid()
136        if cur_pid != getattr(self, '_rng_pid', None):
137            self._rng = _Random()
138            self._rng_pid = cur_pid
139        return self._rng
140
141    def __iter__(self):
142        return self
143
144    def __next__(self):
145        c = self.characters
146        choose = self.rng.choice
147        letters = [choose(c) for dummy in range(8)]
148        return ''.join(letters)
149
150def _candidate_tempdir_list():
151    """Generate a list of candidate temporary directories which
152    _get_default_tempdir will try."""
153
154    dirlist = []
155
156    # First, try the environment.
157    for envname in 'TMPDIR', 'TEMP', 'TMP':
158        dirname = _os.getenv(envname)
159        if dirname: dirlist.append(dirname)
160
161    # Failing that, try OS-specific locations.
162    if _os.name == 'nt':
163        dirlist.extend([ _os.path.expanduser(r'~\AppData\Local\Temp'),
164                         _os.path.expandvars(r'%SYSTEMROOT%\Temp'),
165                         r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ])
166    else:
167        dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ])
168
169    # As a last resort, the current directory.
170    try:
171        dirlist.append(_os.getcwd())
172    except (AttributeError, OSError):
173        dirlist.append(_os.curdir)
174
175    return dirlist
176
177def _get_default_tempdir():
178    """Calculate the default directory to use for temporary files.
179    This routine should be called exactly once.
180
181    We determine whether or not a candidate temp dir is usable by
182    trying to create and write to a file in that directory.  If this
183    is successful, the test file is deleted.  To prevent denial of
184    service, the name of the test file must be randomized."""
185
186    namer = _RandomNameSequence()
187    dirlist = _candidate_tempdir_list()
188
189    for dir in dirlist:
190        if dir != _os.curdir:
191            dir = _os.path.abspath(dir)
192        # Try only a few names per directory.
193        for seq in range(100):
194            name = next(namer)
195            filename = _os.path.join(dir, name)
196            try:
197                fd = _os.open(filename, _bin_openflags, 0o600)
198                try:
199                    try:
200                        with _io.open(fd, 'wb', closefd=False) as fp:
201                            fp.write(b'blat')
202                    finally:
203                        _os.close(fd)
204                finally:
205                    _os.unlink(filename)
206                return dir
207            except FileExistsError:
208                pass
209            except PermissionError:
210                # This exception is thrown when a directory with the chosen name
211                # already exists on windows.
212                if (_os.name == 'nt' and _os.path.isdir(dir) and
213                    _os.access(dir, _os.W_OK)):
214                    continue
215                break   # no point trying more names in this directory
216            except OSError:
217                break   # no point trying more names in this directory
218    raise FileNotFoundError(_errno.ENOENT,
219                            "No usable temporary directory found in %s" %
220                            dirlist)
221
222_name_sequence = None
223
224def _get_candidate_names():
225    """Common setup sequence for all user-callable interfaces."""
226
227    global _name_sequence
228    if _name_sequence is None:
229        _once_lock.acquire()
230        try:
231            if _name_sequence is None:
232                _name_sequence = _RandomNameSequence()
233        finally:
234            _once_lock.release()
235    return _name_sequence
236
237
238def _mkstemp_inner(dir, pre, suf, flags, output_type):
239    """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""
240
241    names = _get_candidate_names()
242    if output_type is bytes:
243        names = map(_os.fsencode, names)
244
245    for seq in range(TMP_MAX):
246        name = next(names)
247        file = _os.path.join(dir, pre + name + suf)
248        _sys.audit("tempfile.mkstemp", file)
249        try:
250            fd = _os.open(file, flags, 0o600)
251        except FileExistsError:
252            continue    # try again
253        except PermissionError:
254            # This exception is thrown when a directory with the chosen name
255            # already exists on windows.
256            if (_os.name == 'nt' and _os.path.isdir(dir) and
257                _os.access(dir, _os.W_OK)):
258                continue
259            else:
260                raise
261        return (fd, _os.path.abspath(file))
262
263    raise FileExistsError(_errno.EEXIST,
264                          "No usable temporary file name found")
265
266
267# User visible interfaces.
268
269def gettempprefix():
270    """The default prefix for temporary directories."""
271    return template
272
273def gettempprefixb():
274    """The default prefix for temporary directories as bytes."""
275    return _os.fsencode(gettempprefix())
276
277tempdir = None
278
279def gettempdir():
280    """Accessor for tempfile.tempdir."""
281    global tempdir
282    if tempdir is None:
283        _once_lock.acquire()
284        try:
285            if tempdir is None:
286                tempdir = _get_default_tempdir()
287        finally:
288            _once_lock.release()
289    return tempdir
290
291def gettempdirb():
292    """A bytes version of tempfile.gettempdir()."""
293    return _os.fsencode(gettempdir())
294
295def mkstemp(suffix=None, prefix=None, dir=None, text=False):
296    """User-callable function to create and return a unique temporary
297    file.  The return value is a pair (fd, name) where fd is the
298    file descriptor returned by os.open, and name is the filename.
299
300    If 'suffix' is not None, the file name will end with that suffix,
301    otherwise there will be no suffix.
302
303    If 'prefix' is not None, the file name will begin with that prefix,
304    otherwise a default prefix is used.
305
306    If 'dir' is not None, the file will be created in that directory,
307    otherwise a default directory is used.
308
309    If 'text' is specified and true, the file is opened in text
310    mode.  Else (the default) the file is opened in binary mode.  On
311    some operating systems, this makes no difference.
312
313    If any of 'suffix', 'prefix' and 'dir' are not None, they must be the
314    same type.  If they are bytes, the returned name will be bytes; str
315    otherwise.
316
317    The file is readable and writable only by the creating user ID.
318    If the operating system uses permission bits to indicate whether a
319    file is executable, the file is executable by no one. The file
320    descriptor is not inherited by children of this process.
321
322    Caller is responsible for deleting the file when done with it.
323    """
324
325    prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)
326
327    if text:
328        flags = _text_openflags
329    else:
330        flags = _bin_openflags
331
332    return _mkstemp_inner(dir, prefix, suffix, flags, output_type)
333
334
335def mkdtemp(suffix=None, prefix=None, dir=None):
336    """User-callable function to create and return a unique temporary
337    directory.  The return value is the pathname of the directory.
338
339    Arguments are as for mkstemp, except that the 'text' argument is
340    not accepted.
341
342    The directory is readable, writable, and searchable only by the
343    creating user.
344
345    Caller is responsible for deleting the directory when done with it.
346    """
347
348    prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)
349
350    names = _get_candidate_names()
351    if output_type is bytes:
352        names = map(_os.fsencode, names)
353
354    for seq in range(TMP_MAX):
355        name = next(names)
356        file = _os.path.join(dir, prefix + name + suffix)
357        _sys.audit("tempfile.mkdtemp", file)
358        try:
359            _os.mkdir(file, 0o700)
360        except FileExistsError:
361            continue    # try again
362        except PermissionError:
363            # This exception is thrown when a directory with the chosen name
364            # already exists on windows.
365            if (_os.name == 'nt' and _os.path.isdir(dir) and
366                _os.access(dir, _os.W_OK)):
367                continue
368            else:
369                raise
370        return file
371
372    raise FileExistsError(_errno.EEXIST,
373                          "No usable temporary directory name found")
374
375def mktemp(suffix="", prefix=template, dir=None):
376    """User-callable function to return a unique temporary file name.  The
377    file is not created.
378
379    Arguments are similar to mkstemp, except that the 'text' argument is
380    not accepted, and suffix=None, prefix=None and bytes file names are not
381    supported.
382
383    THIS FUNCTION IS UNSAFE AND SHOULD NOT BE USED.  The file name may
384    refer to a file that did not exist at some point, but by the time
385    you get around to creating it, someone else may have beaten you to
386    the punch.
387    """
388
389##    from warnings import warn as _warn
390##    _warn("mktemp is a potential security risk to your program",
391##          RuntimeWarning, stacklevel=2)
392
393    if dir is None:
394        dir = gettempdir()
395
396    names = _get_candidate_names()
397    for seq in range(TMP_MAX):
398        name = next(names)
399        file = _os.path.join(dir, prefix + name + suffix)
400        if not _exists(file):
401            return file
402
403    raise FileExistsError(_errno.EEXIST,
404                          "No usable temporary filename found")
405
406
407class _TemporaryFileCloser:
408    """A separate object allowing proper closing of a temporary file's
409    underlying file object, without adding a __del__ method to the
410    temporary file."""
411
412    file = None  # Set here since __del__ checks it
413    close_called = False
414
415    def __init__(self, file, name, delete=True):
416        self.file = file
417        self.name = name
418        self.delete = delete
419
420    # NT provides delete-on-close as a primitive, so we don't need
421    # the wrapper to do anything special.  We still use it so that
422    # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile.
423    if _os.name != 'nt':
424        # Cache the unlinker so we don't get spurious errors at
425        # shutdown when the module-level "os" is None'd out.  Note
426        # that this must be referenced as self.unlink, because the
427        # name TemporaryFileWrapper may also get None'd out before
428        # __del__ is called.
429
430        def close(self, unlink=_os.unlink):
431            if not self.close_called and self.file is not None:
432                self.close_called = True
433                try:
434                    self.file.close()
435                finally:
436                    if self.delete:
437                        unlink(self.name)
438
439        # Need to ensure the file is deleted on __del__
440        def __del__(self):
441            self.close()
442
443    else:
444        def close(self):
445            if not self.close_called:
446                self.close_called = True
447                self.file.close()
448
449
450class _TemporaryFileWrapper:
451    """Temporary file wrapper
452
453    This class provides a wrapper around files opened for
454    temporary use.  In particular, it seeks to automatically
455    remove the file when it is no longer needed.
456    """
457
458    def __init__(self, file, name, delete=True):
459        self.file = file
460        self.name = name
461        self.delete = delete
462        self._closer = _TemporaryFileCloser(file, name, delete)
463
464    def __getattr__(self, name):
465        # Attribute lookups are delegated to the underlying file
466        # and cached for non-numeric results
467        # (i.e. methods are cached, closed and friends are not)
468        file = self.__dict__['file']
469        a = getattr(file, name)
470        if hasattr(a, '__call__'):
471            func = a
472            @_functools.wraps(func)
473            def func_wrapper(*args, **kwargs):
474                return func(*args, **kwargs)
475            # Avoid closing the file as long as the wrapper is alive,
476            # see issue #18879.
477            func_wrapper._closer = self._closer
478            a = func_wrapper
479        if not isinstance(a, int):
480            setattr(self, name, a)
481        return a
482
483    # The underlying __enter__ method returns the wrong object
484    # (self.file) so override it to return the wrapper
485    def __enter__(self):
486        self.file.__enter__()
487        return self
488
489    # Need to trap __exit__ as well to ensure the file gets
490    # deleted when used in a with statement
491    def __exit__(self, exc, value, tb):
492        result = self.file.__exit__(exc, value, tb)
493        self.close()
494        return result
495
496    def close(self):
497        """
498        Close the temporary file, possibly deleting it.
499        """
500        self._closer.close()
501
502    # iter() doesn't use __getattr__ to find the __iter__ method
503    def __iter__(self):
504        # Don't return iter(self.file), but yield from it to avoid closing
505        # file as long as it's being used as iterator (see issue #23700).  We
506        # can't use 'yield from' here because iter(file) returns the file
507        # object itself, which has a close method, and thus the file would get
508        # closed when the generator is finalized, due to PEP380 semantics.
509        for line in self.file:
510            yield line
511
512
513def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None,
514                       newline=None, suffix=None, prefix=None,
515                       dir=None, delete=True, *, errors=None):
516    """Create and return a temporary file.
517    Arguments:
518    'prefix', 'suffix', 'dir' -- as for mkstemp.
519    'mode' -- the mode argument to io.open (default "w+b").
520    'buffering' -- the buffer size argument to io.open (default -1).
521    'encoding' -- the encoding argument to io.open (default None)
522    'newline' -- the newline argument to io.open (default None)
523    'delete' -- whether the file is deleted on close (default True).
524    'errors' -- the errors argument to io.open (default None)
525    The file is created as mkstemp() would do it.
526
527    Returns an object with a file-like interface; the name of the file
528    is accessible as its 'name' attribute.  The file will be automatically
529    deleted when it is closed unless the 'delete' argument is set to False.
530    """
531
532    prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)
533
534    flags = _bin_openflags
535
536    # Setting O_TEMPORARY in the flags causes the OS to delete
537    # the file when it is closed.  This is only supported by Windows.
538    if _os.name == 'nt' and delete:
539        flags |= _os.O_TEMPORARY
540
541    (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
542    try:
543        file = _io.open(fd, mode, buffering=buffering,
544                        newline=newline, encoding=encoding, errors=errors)
545
546        return _TemporaryFileWrapper(file, name, delete)
547    except BaseException:
548        _os.unlink(name)
549        _os.close(fd)
550        raise
551
552if _os.name != 'posix' or _sys.platform == 'cygwin':
553    # On non-POSIX and Cygwin systems, assume that we cannot unlink a file
554    # while it is open.
555    TemporaryFile = NamedTemporaryFile
556
557else:
558    # Is the O_TMPFILE flag available and does it work?
559    # The flag is set to False if os.open(dir, os.O_TMPFILE) raises an
560    # IsADirectoryError exception
561    _O_TMPFILE_WORKS = hasattr(_os, 'O_TMPFILE')
562
563    def TemporaryFile(mode='w+b', buffering=-1, encoding=None,
564                      newline=None, suffix=None, prefix=None,
565                      dir=None, *, errors=None):
566        """Create and return a temporary file.
567        Arguments:
568        'prefix', 'suffix', 'dir' -- as for mkstemp.
569        'mode' -- the mode argument to io.open (default "w+b").
570        'buffering' -- the buffer size argument to io.open (default -1).
571        'encoding' -- the encoding argument to io.open (default None)
572        'newline' -- the newline argument to io.open (default None)
573        'errors' -- the errors argument to io.open (default None)
574        The file is created as mkstemp() would do it.
575
576        Returns an object with a file-like interface.  The file has no
577        name, and will cease to exist when it is closed.
578        """
579        global _O_TMPFILE_WORKS
580
581        prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)
582
583        flags = _bin_openflags
584        if _O_TMPFILE_WORKS:
585            try:
586                flags2 = (flags | _os.O_TMPFILE) & ~_os.O_CREAT
587                fd = _os.open(dir, flags2, 0o600)
588            except IsADirectoryError:
589                # Linux kernel older than 3.11 ignores the O_TMPFILE flag:
590                # O_TMPFILE is read as O_DIRECTORY. Trying to open a directory
591                # with O_RDWR|O_DIRECTORY fails with IsADirectoryError, a
592                # directory cannot be open to write. Set flag to False to not
593                # try again.
594                _O_TMPFILE_WORKS = False
595            except OSError:
596                # The filesystem of the directory does not support O_TMPFILE.
597                # For example, OSError(95, 'Operation not supported').
598                #
599                # On Linux kernel older than 3.11, trying to open a regular
600                # file (or a symbolic link to a regular file) with O_TMPFILE
601                # fails with NotADirectoryError, because O_TMPFILE is read as
602                # O_DIRECTORY.
603                pass
604            else:
605                try:
606                    return _io.open(fd, mode, buffering=buffering,
607                                    newline=newline, encoding=encoding,
608                                    errors=errors)
609                except:
610                    _os.close(fd)
611                    raise
612            # Fallback to _mkstemp_inner().
613
614        (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
615        try:
616            _os.unlink(name)
617            return _io.open(fd, mode, buffering=buffering,
618                            newline=newline, encoding=encoding, errors=errors)
619        except:
620            _os.close(fd)
621            raise
622
623class SpooledTemporaryFile:
624    """Temporary file wrapper, specialized to switch from BytesIO
625    or StringIO to a real file when it exceeds a certain size or
626    when a fileno is needed.
627    """
628    _rolled = False
629
630    def __init__(self, max_size=0, mode='w+b', buffering=-1,
631                 encoding=None, newline=None,
632                 suffix=None, prefix=None, dir=None, *, errors=None):
633        if 'b' in mode:
634            self._file = _io.BytesIO()
635        else:
636            self._file = _io.TextIOWrapper(_io.BytesIO(),
637                            encoding=encoding, errors=errors,
638                            newline=newline)
639        self._max_size = max_size
640        self._rolled = False
641        self._TemporaryFileArgs = {'mode': mode, 'buffering': buffering,
642                                   'suffix': suffix, 'prefix': prefix,
643                                   'encoding': encoding, 'newline': newline,
644                                   'dir': dir, 'errors': errors}
645
646    def _check(self, file):
647        if self._rolled: return
648        max_size = self._max_size
649        if max_size and file.tell() > max_size:
650            self.rollover()
651
652    def rollover(self):
653        if self._rolled: return
654        file = self._file
655        newfile = self._file = TemporaryFile(**self._TemporaryFileArgs)
656        del self._TemporaryFileArgs
657
658        pos = file.tell()
659        if hasattr(newfile, 'buffer'):
660            newfile.buffer.write(file.detach().getvalue())
661        else:
662            newfile.write(file.getvalue())
663        newfile.seek(pos, 0)
664
665        self._rolled = True
666
667    # The method caching trick from NamedTemporaryFile
668    # won't work here, because _file may change from a
669    # BytesIO/StringIO instance to a real file. So we list
670    # all the methods directly.
671
672    # Context management protocol
673    def __enter__(self):
674        if self._file.closed:
675            raise ValueError("Cannot enter context with closed file")
676        return self
677
678    def __exit__(self, exc, value, tb):
679        self._file.close()
680
681    # file protocol
682    def __iter__(self):
683        return self._file.__iter__()
684
685    def close(self):
686        self._file.close()
687
688    @property
689    def closed(self):
690        return self._file.closed
691
692    @property
693    def encoding(self):
694        return self._file.encoding
695
696    @property
697    def errors(self):
698        return self._file.errors
699
700    def fileno(self):
701        self.rollover()
702        return self._file.fileno()
703
704    def flush(self):
705        self._file.flush()
706
707    def isatty(self):
708        return self._file.isatty()
709
710    @property
711    def mode(self):
712        try:
713            return self._file.mode
714        except AttributeError:
715            return self._TemporaryFileArgs['mode']
716
717    @property
718    def name(self):
719        try:
720            return self._file.name
721        except AttributeError:
722            return None
723
724    @property
725    def newlines(self):
726        return self._file.newlines
727
728    def read(self, *args):
729        return self._file.read(*args)
730
731    def readline(self, *args):
732        return self._file.readline(*args)
733
734    def readlines(self, *args):
735        return self._file.readlines(*args)
736
737    def seek(self, *args):
738        return self._file.seek(*args)
739
740    @property
741    def softspace(self):
742        return self._file.softspace
743
744    def tell(self):
745        return self._file.tell()
746
747    def truncate(self, size=None):
748        if size is None:
749            self._file.truncate()
750        else:
751            if size > self._max_size:
752                self.rollover()
753            self._file.truncate(size)
754
755    def write(self, s):
756        file = self._file
757        rv = file.write(s)
758        self._check(file)
759        return rv
760
761    def writelines(self, iterable):
762        file = self._file
763        rv = file.writelines(iterable)
764        self._check(file)
765        return rv
766
767
768class TemporaryDirectory(object):
769    """Create and return a temporary directory.  This has the same
770    behavior as mkdtemp but can be used as a context manager.  For
771    example:
772
773        with TemporaryDirectory() as tmpdir:
774            ...
775
776    Upon exiting the context, the directory and everything contained
777    in it are removed.
778    """
779
780    def __init__(self, suffix=None, prefix=None, dir=None):
781        self.name = mkdtemp(suffix, prefix, dir)
782        self._finalizer = _weakref.finalize(
783            self, self._cleanup, self.name,
784            warn_message="Implicitly cleaning up {!r}".format(self))
785
786    @classmethod
787    def _rmtree(cls, name):
788        def onerror(func, path, exc_info):
789            if issubclass(exc_info[0], PermissionError):
790                def resetperms(path):
791                    try:
792                        _os.chflags(path, 0)
793                    except AttributeError:
794                        pass
795                    _os.chmod(path, 0o700)
796
797                try:
798                    if path != name:
799                        resetperms(_os.path.dirname(path))
800                    resetperms(path)
801
802                    try:
803                        _os.unlink(path)
804                    # PermissionError is raised on FreeBSD for directories
805                    except (IsADirectoryError, PermissionError):
806                        cls._rmtree(path)
807                except FileNotFoundError:
808                    pass
809            elif issubclass(exc_info[0], FileNotFoundError):
810                pass
811            else:
812                raise
813
814        _shutil.rmtree(name, onerror=onerror)
815
816    @classmethod
817    def _cleanup(cls, name, warn_message):
818        cls._rmtree(name)
819        _warnings.warn(warn_message, ResourceWarning)
820
821    def __repr__(self):
822        return "<{} {!r}>".format(self.__class__.__name__, self.name)
823
824    def __enter__(self):
825        return self.name
826
827    def __exit__(self, exc, value, tb):
828        self.cleanup()
829
830    def cleanup(self):
831        if self._finalizer.detach():
832            self._rmtree(self.name)
833