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 types as _types
48import weakref as _weakref
49import _thread
50_allocate_lock = _thread.allocate_lock
51
52_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
53if hasattr(_os, 'O_NOFOLLOW'):
54    _text_openflags |= _os.O_NOFOLLOW
55
56_bin_openflags = _text_openflags
57if hasattr(_os, 'O_BINARY'):
58    _bin_openflags |= _os.O_BINARY
59
60if hasattr(_os, 'TMP_MAX'):
61    TMP_MAX = _os.TMP_MAX
62else:
63    TMP_MAX = 10000
64
65# This variable _was_ unused for legacy reasons, see issue 10354.
66# But as of 3.5 we actually use it at runtime so changing it would
67# have a possibly desirable side effect...  But we do not want to support
68# that as an API.  It is undocumented on purpose.  Do not depend on this.
69template = "tmp"
70
71# Internal routines.
72
73_once_lock = _allocate_lock()
74
75
76def _exists(fn):
77    try:
78        _os.lstat(fn)
79    except OSError:
80        return False
81    else:
82        return True
83
84
85def _infer_return_type(*args):
86    """Look at the type of all args and divine their implied return type."""
87    return_type = None
88    for arg in args:
89        if arg is None:
90            continue
91        if isinstance(arg, bytes):
92            if return_type is str:
93                raise TypeError("Can't mix bytes and non-bytes in "
94                                "path components.")
95            return_type = bytes
96        else:
97            if return_type is bytes:
98                raise TypeError("Can't mix bytes and non-bytes in "
99                                "path components.")
100            return_type = str
101    if return_type is None:
102        return str  # tempfile APIs return a str by default.
103    return return_type
104
105
106def _sanitize_params(prefix, suffix, dir):
107    """Common parameter processing for most APIs in this module."""
108    output_type = _infer_return_type(prefix, suffix, dir)
109    if suffix is None:
110        suffix = output_type()
111    if prefix is None:
112        if output_type is str:
113            prefix = template
114        else:
115            prefix = _os.fsencode(template)
116    if dir is None:
117        if output_type is str:
118            dir = gettempdir()
119        else:
120            dir = gettempdirb()
121    return prefix, suffix, dir, output_type
122
123
124class _RandomNameSequence:
125    """An instance of _RandomNameSequence generates an endless
126    sequence of unpredictable strings which can safely be incorporated
127    into file names.  Each string is eight characters long.  Multiple
128    threads can safely use the same instance at the same time.
129
130    _RandomNameSequence is an iterator."""
131
132    characters = "abcdefghijklmnopqrstuvwxyz0123456789_"
133
134    @property
135    def rng(self):
136        cur_pid = _os.getpid()
137        if cur_pid != getattr(self, '_rng_pid', None):
138            self._rng = _Random()
139            self._rng_pid = cur_pid
140        return self._rng
141
142    def __iter__(self):
143        return self
144
145    def __next__(self):
146        c = self.characters
147        choose = self.rng.choice
148        letters = [choose(c) for dummy in range(8)]
149        return ''.join(letters)
150
151def _candidate_tempdir_list():
152    """Generate a list of candidate temporary directories which
153    _get_default_tempdir will try."""
154
155    dirlist = []
156
157    # First, try the environment.
158    for envname in 'TMPDIR', 'TEMP', 'TMP':
159        dirname = _os.getenv(envname)
160        if dirname: dirlist.append(dirname)
161
162    # Failing that, try OS-specific locations.
163    if _os.name == 'nt':
164        dirlist.extend([ _os.path.expanduser(r'~\AppData\Local\Temp'),
165                         _os.path.expandvars(r'%SYSTEMROOT%\Temp'),
166                         r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ])
167    else:
168        dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ])
169
170    # As a last resort, the current directory.
171    try:
172        dirlist.append(_os.getcwd())
173    except (AttributeError, OSError):
174        dirlist.append(_os.curdir)
175
176    return dirlist
177
178def _get_default_tempdir():
179    """Calculate the default directory to use for temporary files.
180    This routine should be called exactly once.
181
182    We determine whether or not a candidate temp dir is usable by
183    trying to create and write to a file in that directory.  If this
184    is successful, the test file is deleted.  To prevent denial of
185    service, the name of the test file must be randomized."""
186
187    namer = _RandomNameSequence()
188    dirlist = _candidate_tempdir_list()
189
190    for dir in dirlist:
191        if dir != _os.curdir:
192            dir = _os.path.abspath(dir)
193        # Try only a few names per directory.
194        for seq in range(100):
195            name = next(namer)
196            filename = _os.path.join(dir, name)
197            try:
198                fd = _os.open(filename, _bin_openflags, 0o600)
199                try:
200                    try:
201                        with _io.open(fd, 'wb', closefd=False) as fp:
202                            fp.write(b'blat')
203                    finally:
204                        _os.close(fd)
205                finally:
206                    _os.unlink(filename)
207                return dir
208            except FileExistsError:
209                pass
210            except PermissionError:
211                # This exception is thrown when a directory with the chosen name
212                # already exists on windows.
213                if (_os.name == 'nt' and _os.path.isdir(dir) and
214                    _os.access(dir, _os.W_OK)):
215                    continue
216                break   # no point trying more names in this directory
217            except OSError:
218                break   # no point trying more names in this directory
219    raise FileNotFoundError(_errno.ENOENT,
220                            "No usable temporary directory found in %s" %
221                            dirlist)
222
223_name_sequence = None
224
225def _get_candidate_names():
226    """Common setup sequence for all user-callable interfaces."""
227
228    global _name_sequence
229    if _name_sequence is None:
230        _once_lock.acquire()
231        try:
232            if _name_sequence is None:
233                _name_sequence = _RandomNameSequence()
234        finally:
235            _once_lock.release()
236    return _name_sequence
237
238
239def _mkstemp_inner(dir, pre, suf, flags, output_type):
240    """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""
241
242    names = _get_candidate_names()
243    if output_type is bytes:
244        names = map(_os.fsencode, names)
245
246    for seq in range(TMP_MAX):
247        name = next(names)
248        file = _os.path.join(dir, pre + name + suf)
249        _sys.audit("tempfile.mkstemp", file)
250        try:
251            fd = _os.open(file, flags, 0o600)
252        except FileExistsError:
253            continue    # try again
254        except PermissionError:
255            # This exception is thrown when a directory with the chosen name
256            # already exists on windows.
257            if (_os.name == 'nt' and _os.path.isdir(dir) and
258                _os.access(dir, _os.W_OK)):
259                continue
260            else:
261                raise
262        return (fd, _os.path.abspath(file))
263
264    raise FileExistsError(_errno.EEXIST,
265                          "No usable temporary file name found")
266
267
268# User visible interfaces.
269
270def gettempprefix():
271    """The default prefix for temporary directories."""
272    return template
273
274def gettempprefixb():
275    """The default prefix for temporary directories as bytes."""
276    return _os.fsencode(gettempprefix())
277
278tempdir = None
279
280def gettempdir():
281    """Accessor for tempfile.tempdir."""
282    global tempdir
283    if tempdir is None:
284        _once_lock.acquire()
285        try:
286            if tempdir is None:
287                tempdir = _get_default_tempdir()
288        finally:
289            _once_lock.release()
290    return tempdir
291
292def gettempdirb():
293    """A bytes version of tempfile.gettempdir()."""
294    return _os.fsencode(gettempdir())
295
296def mkstemp(suffix=None, prefix=None, dir=None, text=False):
297    """User-callable function to create and return a unique temporary
298    file.  The return value is a pair (fd, name) where fd is the
299    file descriptor returned by os.open, and name is the filename.
300
301    If 'suffix' is not None, the file name will end with that suffix,
302    otherwise there will be no suffix.
303
304    If 'prefix' is not None, the file name will begin with that prefix,
305    otherwise a default prefix is used.
306
307    If 'dir' is not None, the file will be created in that directory,
308    otherwise a default directory is used.
309
310    If 'text' is specified and true, the file is opened in text
311    mode.  Else (the default) the file is opened in binary mode.
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    __class_getitem__ = classmethod(_types.GenericAlias)
647
648    def _check(self, file):
649        if self._rolled: return
650        max_size = self._max_size
651        if max_size and file.tell() > max_size:
652            self.rollover()
653
654    def rollover(self):
655        if self._rolled: return
656        file = self._file
657        newfile = self._file = TemporaryFile(**self._TemporaryFileArgs)
658        del self._TemporaryFileArgs
659
660        pos = file.tell()
661        if hasattr(newfile, 'buffer'):
662            newfile.buffer.write(file.detach().getvalue())
663        else:
664            newfile.write(file.getvalue())
665        newfile.seek(pos, 0)
666
667        self._rolled = True
668
669    # The method caching trick from NamedTemporaryFile
670    # won't work here, because _file may change from a
671    # BytesIO/StringIO instance to a real file. So we list
672    # all the methods directly.
673
674    # Context management protocol
675    def __enter__(self):
676        if self._file.closed:
677            raise ValueError("Cannot enter context with closed file")
678        return self
679
680    def __exit__(self, exc, value, tb):
681        self._file.close()
682
683    # file protocol
684    def __iter__(self):
685        return self._file.__iter__()
686
687    def close(self):
688        self._file.close()
689
690    @property
691    def closed(self):
692        return self._file.closed
693
694    @property
695    def encoding(self):
696        return self._file.encoding
697
698    @property
699    def errors(self):
700        return self._file.errors
701
702    def fileno(self):
703        self.rollover()
704        return self._file.fileno()
705
706    def flush(self):
707        self._file.flush()
708
709    def isatty(self):
710        return self._file.isatty()
711
712    @property
713    def mode(self):
714        try:
715            return self._file.mode
716        except AttributeError:
717            return self._TemporaryFileArgs['mode']
718
719    @property
720    def name(self):
721        try:
722            return self._file.name
723        except AttributeError:
724            return None
725
726    @property
727    def newlines(self):
728        return self._file.newlines
729
730    def read(self, *args):
731        return self._file.read(*args)
732
733    def readline(self, *args):
734        return self._file.readline(*args)
735
736    def readlines(self, *args):
737        return self._file.readlines(*args)
738
739    def seek(self, *args):
740        return self._file.seek(*args)
741
742    def tell(self):
743        return self._file.tell()
744
745    def truncate(self, size=None):
746        if size is None:
747            self._file.truncate()
748        else:
749            if size > self._max_size:
750                self.rollover()
751            self._file.truncate(size)
752
753    def write(self, s):
754        file = self._file
755        rv = file.write(s)
756        self._check(file)
757        return rv
758
759    def writelines(self, iterable):
760        file = self._file
761        rv = file.writelines(iterable)
762        self._check(file)
763        return rv
764
765
766class TemporaryDirectory(object):
767    """Create and return a temporary directory.  This has the same
768    behavior as mkdtemp but can be used as a context manager.  For
769    example:
770
771        with TemporaryDirectory() as tmpdir:
772            ...
773
774    Upon exiting the context, the directory and everything contained
775    in it are removed.
776    """
777
778    def __init__(self, suffix=None, prefix=None, dir=None):
779        self.name = mkdtemp(suffix, prefix, dir)
780        self._finalizer = _weakref.finalize(
781            self, self._cleanup, self.name,
782            warn_message="Implicitly cleaning up {!r}".format(self))
783
784    @classmethod
785    def _rmtree(cls, name):
786        def onerror(func, path, exc_info):
787            if issubclass(exc_info[0], PermissionError):
788                def resetperms(path):
789                    try:
790                        _os.chflags(path, 0)
791                    except AttributeError:
792                        pass
793                    _os.chmod(path, 0o700)
794
795                try:
796                    if path != name:
797                        resetperms(_os.path.dirname(path))
798                    resetperms(path)
799
800                    try:
801                        _os.unlink(path)
802                    # PermissionError is raised on FreeBSD for directories
803                    except (IsADirectoryError, PermissionError):
804                        cls._rmtree(path)
805                except FileNotFoundError:
806                    pass
807            elif issubclass(exc_info[0], FileNotFoundError):
808                pass
809            else:
810                raise
811
812        _shutil.rmtree(name, onerror=onerror)
813
814    @classmethod
815    def _cleanup(cls, name, warn_message):
816        cls._rmtree(name)
817        _warnings.warn(warn_message, ResourceWarning)
818
819    def __repr__(self):
820        return "<{} {!r}>".format(self.__class__.__name__, self.name)
821
822    def __enter__(self):
823        return self.name
824
825    def __exit__(self, exc, value, tb):
826        self.cleanup()
827
828    def cleanup(self):
829        if self._finalizer.detach():
830            self._rmtree(self.name)
831
832    __class_getitem__ = classmethod(_types.GenericAlias)
833