codekingpro/portable-devtools
114k
1"""Temporary files.2 3This module provides generic, low- and high-level interfaces for4creating temporary files and directories. All of the interfaces5provided by this module can be used without fear of race conditions6except for 'mktemp'. 'mktemp' is subject to race conditions and7should not be used; it is provided for backward compatibility only.8 9The default path names are returned as str. If you supply bytes as10input, 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 before20 giving up.21 tempdir - If this is set to a string before the first use of22 any routine from this module, it will be considered as23 another candidate location to store temporary files.24"""25 26__all__ = [27 "NamedTemporaryFile", "TemporaryFile", # high level safe interfaces28 "SpooledTemporaryFile", "TemporaryDirectory",29 "mkstemp", "mkdtemp", # low level safe interfaces30 "mktemp", # deprecated unsafe interface31 "TMP_MAX", "gettempprefix", # constants32 "tempdir", "gettempdir",33 "gettempprefixb", "gettempdirb",34 ]35 36 37# Imports.38 39import functools as _functools40import warnings as _warnings41import io as _io42import os as _os43import shutil as _shutil44import errno as _errno45from random import Random as _Random46import sys as _sys47import types as _types48import weakref as _weakref49import _thread50_allocate_lock = _thread.allocate_lock51 52_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL53if hasattr(_os, 'O_NOFOLLOW'):54 _text_openflags |= _os.O_NOFOLLOW55 56_bin_openflags = _text_openflags57if hasattr(_os, 'O_BINARY'):58 _bin_openflags |= _os.O_BINARY59 60# This is more than enough.61# Each name contains over 40 random bits. Even with a million temporary62# files, the chance of a conflict is less than 1 in a million, and with63# 20 attempts, it is less than 1e-120.64TMP_MAX = 2065 66# This variable _was_ unused for legacy reasons, see issue 10354.67# But as of 3.5 we actually use it at runtime so changing it would68# have a possibly desirable side effect... But we do not want to support69# that as an API. It is undocumented on purpose. Do not depend on this.70template = "tmp"71 72# Internal routines.73 74_once_lock = _allocate_lock()75 76 77def _exists(fn):78 try:79 _os.lstat(fn)80 except OSError:81 return False82 else:83 return True84 85 86def _infer_return_type(*args):87 """Look at the type of all args and divine their implied return type."""88 return_type = None89 for arg in args:90 if arg is None:91 continue92 93 if isinstance(arg, _os.PathLike):94 arg = _os.fspath(arg)95 96 if isinstance(arg, bytes):97 if return_type is str:98 raise TypeError("Can't mix bytes and non-bytes in "99 "path components.")100 return_type = bytes101 else:102 if return_type is bytes:103 raise TypeError("Can't mix bytes and non-bytes in "104 "path components.")105 return_type = str106 if return_type is None:107 if tempdir is None or isinstance(tempdir, str):108 return str # tempfile APIs return a str by default.109 else:110 # we could check for bytes but it'll fail later on anyway111 return bytes112 return return_type113 114 115def _sanitize_params(prefix, suffix, dir):116 """Common parameter processing for most APIs in this module."""117 output_type = _infer_return_type(prefix, suffix, dir)118 if suffix is None:119 suffix = output_type()120 if prefix is None:121 if output_type is str:122 prefix = template123 else:124 prefix = _os.fsencode(template)125 if dir is None:126 if output_type is str:127 dir = gettempdir()128 else:129 dir = gettempdirb()130 return prefix, suffix, dir, output_type131 132 133class _RandomNameSequence:134 """An instance of _RandomNameSequence generates an endless135 sequence of unpredictable strings which can safely be incorporated136 into file names. Each string is eight characters long. Multiple137 threads can safely use the same instance at the same time.138 139 _RandomNameSequence is an iterator."""140 141 characters = "abcdefghijklmnopqrstuvwxyz0123456789_"142 143 @property144 def rng(self):145 cur_pid = _os.getpid()146 if cur_pid != getattr(self, '_rng_pid', None):147 self._rng = _Random()148 self._rng_pid = cur_pid149 return self._rng150 151 def __iter__(self):152 return self153 154 def __next__(self):155 return ''.join(self.rng.choices(self.characters, k=8))156 157def _candidate_tempdir_list():158 """Generate a list of candidate temporary directories which159 _get_default_tempdir will try."""160 161 dirlist = []162 163 # First, try the environment.164 for envname in 'TMPDIR', 'TEMP', 'TMP':165 dirname = _os.getenv(envname)166 if dirname: dirlist.append(dirname)167 168 # Failing that, try OS-specific locations.169 if _os.name == 'nt':170 dirlist.extend([ _os.path.expanduser(r'~\AppData\Local\Temp'),171 _os.path.expandvars(r'%SYSTEMROOT%\Temp'),172 r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ])173 else:174 dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ])175 176 # As a last resort, the current directory.177 try:178 dirlist.append(_os.getcwd())179 except (AttributeError, OSError):180 dirlist.append(_os.curdir)181 182 return dirlist183 184def _get_default_tempdir(dirlist=None):185 """Calculate the default directory to use for temporary files.186 This routine should be called exactly once.187 188 We determine whether or not a candidate temp dir is usable by189 trying to create and write to a file in that directory. If this190 is successful, the test file is deleted. To prevent denial of191 service, the name of the test file must be randomized."""192 193 namer = _RandomNameSequence()194 if dirlist is None:195 dirlist = _candidate_tempdir_list()196 197 for dir in dirlist:198 if dir != _os.curdir:199 dir = _os.path.abspath(dir)200 for seq in range(TMP_MAX):201 name = next(namer)202 filename = _os.path.join(dir, name)203 try:204 fd = _os.open(filename, _bin_openflags, 0o600)205 try:206 try:207 _os.write(fd, b'blat')208 finally:209 _os.close(fd)210 finally:211 _os.unlink(filename)212 return dir213 except FileExistsError:214 pass215 except PermissionError:216 # See the comment in mkdtemp().217 if _os.name == 'nt' and _os.path.isdir(dir):218 continue219 break # no point trying more names in this directory220 except OSError:221 break # no point trying more names in this directory222 raise FileNotFoundError(_errno.ENOENT,223 "No usable temporary directory found in %s" %224 dirlist)225 226_name_sequence = None227 228def _get_candidate_names():229 """Common setup sequence for all user-callable interfaces."""230 231 global _name_sequence232 if _name_sequence is None:233 _once_lock.acquire()234 try:235 if _name_sequence is None:236 _name_sequence = _RandomNameSequence()237 finally:238 _once_lock.release()239 return _name_sequence240 241 242def _mkstemp_inner(dir, pre, suf, flags, output_type):243 """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile."""244 245 dir = _os.path.abspath(dir)246 names = _get_candidate_names()247 if output_type is bytes:248 names = map(_os.fsencode, names)249 250 for seq in range(TMP_MAX):251 name = next(names)252 file = _os.path.join(dir, pre + name + suf)253 _sys.audit("tempfile.mkstemp", file)254 try:255 fd = _os.open(file, flags, 0o600)256 except FileExistsError:257 continue # try again258 except PermissionError:259 # See the comment in mkdtemp().260 if _os.name == 'nt' and _os.path.isdir(dir) and seq < TMP_MAX - 1:261 continue262 else:263 raise264 return fd, file265 266 raise FileExistsError(_errno.EEXIST,267 "No usable temporary file name found")268 269def _dont_follow_symlinks(func, path, *args):270 # Pass follow_symlinks=False, unless not supported on this platform.271 if func in _os.supports_follow_symlinks:272 func(path, *args, follow_symlinks=False)273 elif not _os.path.islink(path):274 func(path, *args)275 276def _resetperms(path):277 try:278 chflags = _os.chflags279 except AttributeError:280 pass281 else:282 _dont_follow_symlinks(chflags, path, 0)283 _dont_follow_symlinks(_os.chmod, path, 0o700)284 285 286# User visible interfaces.287 288def gettempprefix():289 """The default prefix for temporary directories as string."""290 return _os.fsdecode(template)291 292def gettempprefixb():293 """The default prefix for temporary directories as bytes."""294 return _os.fsencode(template)295 296tempdir = None297 298def _gettempdir():299 """Private accessor for tempfile.tempdir."""300 global tempdir301 if tempdir is None:302 _once_lock.acquire()303 try:304 if tempdir is None:305 tempdir = _get_default_tempdir()306 finally:307 _once_lock.release()308 return tempdir309 310def gettempdir():311 """Returns tempfile.tempdir as str."""312 return _os.fsdecode(_gettempdir())313 314def gettempdirb():315 """Returns tempfile.tempdir as bytes."""316 return _os.fsencode(_gettempdir())317 318def mkstemp(suffix=None, prefix=None, dir=None, text=False):319 """User-callable function to create and return a unique temporary320 file. The return value is a pair (fd, name) where fd is the321 file descriptor returned by os.open, and name is the filename.322 323 If 'suffix' is not None, the file name will end with that suffix,324 otherwise there will be no suffix.325 326 If 'prefix' is not None, the file name will begin with that prefix,327 otherwise a default prefix is used.328 329 If 'dir' is not None, the file will be created in that directory,330 otherwise a default directory is used.331 332 If 'text' is specified and true, the file is opened in text333 mode. Else (the default) the file is opened in binary mode.334 335 If any of 'suffix', 'prefix' and 'dir' are not None, they must be the336 same type. If they are bytes, the returned name will be bytes; str337 otherwise.338 339 The file is readable and writable only by the creating user ID.340 If the operating system uses permission bits to indicate whether a341 file is executable, the file is executable by no one. The file342 descriptor is not inherited by children of this process.343 344 Caller is responsible for deleting the file when done with it.345 """346 347 prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)348 349 if text:350 flags = _text_openflags351 else:352 flags = _bin_openflags353 354 return _mkstemp_inner(dir, prefix, suffix, flags, output_type)355 356 357def mkdtemp(suffix=None, prefix=None, dir=None):358 """User-callable function to create and return a unique temporary359 directory. The return value is the pathname of the directory.360 361 Arguments are as for mkstemp, except that the 'text' argument is362 not accepted.363 364 The directory is readable, writable, and searchable only by the365 creating user.366 367 Caller is responsible for deleting the directory when done with it.368 """369 370 prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)371 372 names = _get_candidate_names()373 if output_type is bytes:374 names = map(_os.fsencode, names)375 376 for seq in range(TMP_MAX):377 name = next(names)378 file = _os.path.join(dir, prefix + name + suffix)379 _sys.audit("tempfile.mkdtemp", file)380 try:381 _os.mkdir(file, 0o700)382 except FileExistsError:383 continue # try again384 except PermissionError:385 # On Posix, this exception is raised when the user has no386 # write access to the parent directory.387 # On Windows, it is also raised when a directory with388 # the chosen name already exists, or if the parent directory389 # is not a directory.390 # We cannot distinguish between "directory-exists-error" and391 # "access-denied-error".392 if _os.name == 'nt' and _os.path.isdir(dir) and seq < TMP_MAX - 1:393 continue394 else:395 raise396 return _os.path.abspath(file)397 398 raise FileExistsError(_errno.EEXIST,399 "No usable temporary directory name found")400 401def mktemp(suffix="", prefix=template, dir=None):402 """User-callable function to return a unique temporary file name. The403 file is not created.404 405 Arguments are similar to mkstemp, except that the 'text' argument is406 not accepted, and suffix=None, prefix=None and bytes file names are not407 supported.408 409 THIS FUNCTION IS UNSAFE AND SHOULD NOT BE USED. The file name may410 refer to a file that did not exist at some point, but by the time411 you get around to creating it, someone else may have beaten you to412 the punch.413 """414 415## from warnings import warn as _warn416## _warn("mktemp is a potential security risk to your program",417## RuntimeWarning, stacklevel=2)418 419 if dir is None:420 dir = gettempdir()421 422 names = _get_candidate_names()423 for seq in range(TMP_MAX):424 name = next(names)425 file = _os.path.join(dir, prefix + name + suffix)426 if not _exists(file):427 return file428 429 raise FileExistsError(_errno.EEXIST,430 "No usable temporary filename found")431 432 433class _TemporaryFileCloser:434 """A separate object allowing proper closing of a temporary file's435 underlying file object, without adding a __del__ method to the436 temporary file."""437 438 cleanup_called = False439 close_called = False440 441 def __init__(442 self,443 file,444 name,445 delete=True,446 delete_on_close=True,447 warn_message="Implicitly cleaning up unknown file",448 ):449 self.file = file450 self.name = name451 self.delete = delete452 self.delete_on_close = delete_on_close453 self.warn_message = warn_message454 455 def cleanup(self, windows=(_os.name == 'nt'), unlink=_os.unlink):456 if not self.cleanup_called:457 self.cleanup_called = True458 try:459 if not self.close_called:460 self.close_called = True461 self.file.close()462 finally:463 # Windows provides delete-on-close as a primitive, in which464 # case the file was deleted by self.file.close().465 if self.delete and not (windows and self.delete_on_close):466 try:467 unlink(self.name)468 except FileNotFoundError:469 pass470 471 def close(self):472 if not self.close_called:473 self.close_called = True474 try:475 self.file.close()476 finally:477 if self.delete and self.delete_on_close:478 self.cleanup()479 480 def __del__(self):481 close_called = self.close_called482 self.cleanup()483 if not close_called:484 _warnings.warn(self.warn_message, ResourceWarning)485 486 487class _TemporaryFileWrapper:488 """Temporary file wrapper489 490 This class provides a wrapper around files opened for491 temporary use. In particular, it seeks to automatically492 remove the file when it is no longer needed.493 """494 495 def __init__(self, file, name, delete=True, delete_on_close=True):496 self.file = file497 self.name = name498 self._closer = _TemporaryFileCloser(499 file,500 name,501 delete,502 delete_on_close,503 warn_message=f"Implicitly cleaning up {self!r}",504 )505 506 def __repr__(self):507 file = self.__dict__['file']508 return f"<{type(self).__name__} {file=}>"509 510 def __getattr__(self, name):511 # Attribute lookups are delegated to the underlying file512 # and cached for non-numeric results513 # (i.e. methods are cached, closed and friends are not)514 file = self.__dict__['file']515 a = getattr(file, name)516 if hasattr(a, '__call__'):517 func = a518 @_functools.wraps(func)519 def func_wrapper(*args, **kwargs):520 return func(*args, **kwargs)521 # Avoid closing the file as long as the wrapper is alive,522 # see issue #18879.523 func_wrapper._closer = self._closer524 a = func_wrapper525 if not isinstance(a, int):526 setattr(self, name, a)527 return a528 529 # The underlying __enter__ method returns the wrong object530 # (self.file) so override it to return the wrapper531 def __enter__(self):532 self.file.__enter__()533 return self534 535 # Need to trap __exit__ as well to ensure the file gets536 # deleted when used in a with statement537 def __exit__(self, exc, value, tb):538 result = self.file.__exit__(exc, value, tb)539 self._closer.cleanup()540 return result541 542 def close(self):543 """544 Close the temporary file, possibly deleting it.545 """546 self._closer.close()547 548 # iter() doesn't use __getattr__ to find the __iter__ method549 def __iter__(self):550 # Don't return iter(self.file), but yield from it to avoid closing551 # file as long as it's being used as iterator (see issue #23700). We552 # can't use 'yield from' here because iter(file) returns the file553 # object itself, which has a close method, and thus the file would get554 # closed when the generator is finalized, due to PEP380 semantics.555 for line in self.file:556 yield line557 558def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None,559 newline=None, suffix=None, prefix=None,560 dir=None, delete=True, *, errors=None,561 delete_on_close=True):562 """Create and return a temporary file.563 Arguments:564 'prefix', 'suffix', 'dir' -- as for mkstemp.565 'mode' -- the mode argument to io.open (default "w+b").566 'buffering' -- the buffer size argument to io.open (default -1).567 'encoding' -- the encoding argument to io.open (default None)568 'newline' -- the newline argument to io.open (default None)569 'delete' -- whether the file is automatically deleted (default True).570 'delete_on_close' -- if 'delete', whether the file is deleted on close571 (default True) or otherwise either on context manager exit572 (if context manager was used) or on object finalization. .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 name of the file577 is accessible as its 'name' attribute. The file will be automatically578 deleted when it is closed unless the 'delete' argument is set to False.579 580 On POSIX, NamedTemporaryFiles cannot be automatically deleted if581 the creating process is terminated abruptly with a SIGKILL signal.582 Windows can delete the file even in this case.583 """584 585 prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)586 587 flags = _bin_openflags588 589 # Setting O_TEMPORARY in the flags causes the OS to delete590 # the file when it is closed. This is only supported by Windows.591 if _os.name == 'nt' and delete and delete_on_close:592 flags |= _os.O_TEMPORARY593 594 if "b" not in mode:595 encoding = _io.text_encoding(encoding)596 597 name = None598 def opener(*args):599 nonlocal name600 fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type)601 return fd602 try:603 file = _io.open(dir, mode, buffering=buffering,604 newline=newline, encoding=encoding, errors=errors,605 opener=opener)606 try:607 raw = getattr(file, 'buffer', file)608 raw = getattr(raw, 'raw', raw)609 raw.name = name610 return _TemporaryFileWrapper(file, name, delete, delete_on_close)611 except:612 file.close()613 raise614 except:615 if name is not None and not (616 _os.name == 'nt' and delete and delete_on_close):617 _os.unlink(name)618 raise619 620if _os.name != 'posix' or _sys.platform == 'cygwin':621 # On non-POSIX and Cygwin systems, assume that we cannot unlink a file622 # while it is open.623 TemporaryFile = NamedTemporaryFile624 625else:626 # Is the O_TMPFILE flag available and does it work?627 # The flag is set to False if os.open(dir, os.O_TMPFILE) raises an628 # IsADirectoryError exception629 _O_TMPFILE_WORKS = hasattr(_os, 'O_TMPFILE')630 631 def TemporaryFile(mode='w+b', buffering=-1, encoding=None,632 newline=None, suffix=None, prefix=None,633 dir=None, *, errors=None):634 """Create and return a temporary file.635 Arguments:636 'prefix', 'suffix', 'dir' -- as for mkstemp.637 'mode' -- the mode argument to io.open (default "w+b").638 'buffering' -- the buffer size argument to io.open (default -1).639 'encoding' -- the encoding argument to io.open (default None)640 'newline' -- the newline argument to io.open (default None)641 'errors' -- the errors argument to io.open (default None)642 The file is created as mkstemp() would do it.643 644 Returns an object with a file-like interface. The file has no645 name, and will cease to exist when it is closed.646 """647 global _O_TMPFILE_WORKS648 649 if "b" not in mode:650 encoding = _io.text_encoding(encoding)651 652 prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)653 654 flags = _bin_openflags655 if _O_TMPFILE_WORKS:656 fd = None657 def opener(*args):658 nonlocal fd659 flags2 = (flags | _os.O_TMPFILE) & ~_os.O_CREAT660 fd = _os.open(dir, flags2, 0o600)661 return fd662 try:663 file = _io.open(dir, mode, buffering=buffering,664 newline=newline, encoding=encoding,665 errors=errors, opener=opener)666 raw = getattr(file, 'buffer', file)667 raw = getattr(raw, 'raw', raw)668 raw.name = fd669 return file670 except IsADirectoryError:671 # Linux kernel older than 3.11 ignores the O_TMPFILE flag:672 # O_TMPFILE is read as O_DIRECTORY. Trying to open a directory673 # with O_RDWR|O_DIRECTORY fails with IsADirectoryError, a674 # directory cannot be open to write. Set flag to False to not675 # try again.676 _O_TMPFILE_WORKS = False677 except OSError:678 # The filesystem of the directory does not support O_TMPFILE.679 # For example, OSError(95, 'Operation not supported').680 #681 # On Linux kernel older than 3.11, trying to open a regular682 # file (or a symbolic link to a regular file) with O_TMPFILE683 # fails with NotADirectoryError, because O_TMPFILE is read as684 # O_DIRECTORY.685 pass686 # Fallback to _mkstemp_inner().687 688 fd = None689 def opener(*args):690 nonlocal fd691 fd, name = _mkstemp_inner(dir, prefix, suffix, flags, output_type)692 try:693 _os.unlink(name)694 except BaseException as e:695 _os.close(fd)696 raise697 return fd698 file = _io.open(dir, mode, buffering=buffering,699 newline=newline, encoding=encoding, errors=errors,700 opener=opener)701 raw = getattr(file, 'buffer', file)702 raw = getattr(raw, 'raw', raw)703 raw.name = fd704 return file705 706class SpooledTemporaryFile(_io.IOBase):707 """Temporary file wrapper, specialized to switch from BytesIO708 or StringIO to a real file when it exceeds a certain size or709 when a fileno is needed.710 """711 _rolled = False712 713 def __init__(self, max_size=0, mode='w+b', buffering=-1,714 encoding=None, newline=None,715 suffix=None, prefix=None, dir=None, *, errors=None):716 if 'b' in mode:717 self._file = _io.BytesIO()718 else:719 encoding = _io.text_encoding(encoding)720 self._file = _io.TextIOWrapper(_io.BytesIO(),721 encoding=encoding, errors=errors,722 newline=newline)723 self._max_size = max_size724 self._rolled = False725 self._TemporaryFileArgs = {'mode': mode, 'buffering': buffering,726 'suffix': suffix, 'prefix': prefix,727 'encoding': encoding, 'newline': newline,728 'dir': dir, 'errors': errors}729 730 __class_getitem__ = classmethod(_types.GenericAlias)731 732 def _check(self, file):733 if self._rolled: return734 max_size = self._max_size735 if max_size and file.tell() > max_size:736 self.rollover()737 738 def rollover(self):739 if self._rolled: return740 file = self._file741 newfile = self._file = TemporaryFile(**self._TemporaryFileArgs)742 del self._TemporaryFileArgs743 744 pos = file.tell()745 if hasattr(newfile, 'buffer'):746 newfile.buffer.write(file.detach().getvalue())747 else:748 newfile.write(file.getvalue())749 newfile.seek(pos, 0)750 751 self._rolled = True752 753 # The method caching trick from NamedTemporaryFile754 # won't work here, because _file may change from a755 # BytesIO/StringIO instance to a real file. So we list756 # all the methods directly.757 758 # Context management protocol759 def __enter__(self):760 if self._file.closed:761 raise ValueError("Cannot enter context with closed file")762 return self763 764 def __exit__(self, exc, value, tb):765 self._file.close()766 767 # file protocol768 def __iter__(self):769 return self._file.__iter__()770 771 def __del__(self):772 if not self.closed:773 _warnings.warn(774 "Unclosed file {!r}".format(self),775 ResourceWarning,776 stacklevel=2,777 source=self778 )779 self.close()780 781 def close(self):782 self._file.close()783 784 @property785 def closed(self):786 return self._file.closed787 788 @property789 def encoding(self):790 return self._file.encoding791 792 @property793 def errors(self):794 return self._file.errors795 796 def fileno(self):797 self.rollover()798 return self._file.fileno()799 800 def flush(self):801 self._file.flush()802 803 def isatty(self):804 return self._file.isatty()805 806 @property807 def mode(self):808 try:809 return self._file.mode810 except AttributeError:811 return self._TemporaryFileArgs['mode']812 813 @property814 def name(self):815 try:816 return self._file.name817 except AttributeError:818 return None819 820 @property821 def newlines(self):822 return self._file.newlines823 824 def readable(self):825 return self._file.readable()826 827 def read(self, *args):828 return self._file.read(*args)829 830 def read1(self, *args):831 return self._file.read1(*args)832 833 def readinto(self, b):834 return self._file.readinto(b)835 836 def readinto1(self, b):837 return self._file.readinto1(b)838 839 def readline(self, *args):840 return self._file.readline(*args)841 842 def readlines(self, *args):843 return self._file.readlines(*args)844 845 def seekable(self):846 return self._file.seekable()847 848 def seek(self, *args):849 return self._file.seek(*args)850 851 def tell(self):852 return self._file.tell()853 854 def truncate(self, size=None):855 if size is None:856 return self._file.truncate()857 else:858 if size > self._max_size:859 self.rollover()860 return self._file.truncate(size)861 862 def writable(self):863 return self._file.writable()864 865 def write(self, s):866 file = self._file867 rv = file.write(s)868 self._check(file)869 return rv870 871 def writelines(self, iterable):872 if self._max_size == 0 or self._rolled:873 return self._file.writelines(iterable)874 875 it = iter(iterable)876 for line in it:877 self.write(line)878 if self._rolled:879 return self._file.writelines(it)880 881 def detach(self):882 return self._file.detach()883 884 885class TemporaryDirectory:886 """Create and return a temporary directory. This has the same887 behavior as mkdtemp but can be used as a context manager. For888 example:889 890 with TemporaryDirectory() as tmpdir:891 ...892 893 Upon exiting the context, the directory and everything contained894 in it are removed (unless delete=False is passed or an exception895 is raised during cleanup and ignore_cleanup_errors is not True).896 897 Optional Arguments:898 suffix - A str suffix for the directory name. (see mkdtemp)899 prefix - A str prefix for the directory name. (see mkdtemp)900 dir - A directory to create this temp dir in. (see mkdtemp)901 ignore_cleanup_errors - False; ignore exceptions during cleanup?902 delete - True; whether the directory is automatically deleted.903 """904 905 def __init__(self, suffix=None, prefix=None, dir=None,906 ignore_cleanup_errors=False, *, delete=True):907 self.name = mkdtemp(suffix, prefix, dir)908 self._ignore_cleanup_errors = ignore_cleanup_errors909 self._delete = delete910 self._finalizer = _weakref.finalize(911 self, self._cleanup, self.name,912 warn_message="Implicitly cleaning up {!r}".format(self),913 ignore_errors=self._ignore_cleanup_errors, delete=self._delete)914 915 @classmethod916 def _rmtree(cls, name, ignore_errors=False, repeated=False):917 def onexc(func, path, exc):918 if isinstance(exc, PermissionError):919 if repeated and path == name:920 if ignore_errors:921 return922 raise923 924 try:925 if path != name:926 _resetperms(_os.path.dirname(path))927 _resetperms(path)928 929 try:930 _os.unlink(path)931 except IsADirectoryError:932 cls._rmtree(path, ignore_errors=ignore_errors)933 except PermissionError:934 # The PermissionError handler was originally added for935 # FreeBSD in directories, but it seems that it is raised936 # on Windows too.937 # bpo-43153: Calling _rmtree again may938 # raise NotADirectoryError and mask the PermissionError.939 # So we must re-raise the current PermissionError if940 # path is not a directory.941 if not _os.path.isdir(path) or _os.path.isjunction(path):942 if ignore_errors:943 return944 raise945 cls._rmtree(path, ignore_errors=ignore_errors,946 repeated=(path == name))947 except FileNotFoundError:948 pass949 elif isinstance(exc, FileNotFoundError):950 pass951 else:952 if not ignore_errors:953 raise954 955 _shutil.rmtree(name, onexc=onexc)956 957 @classmethod958 def _cleanup(cls, name, warn_message, ignore_errors=False, delete=True):959 if delete:960 cls._rmtree(name, ignore_errors=ignore_errors)961 _warnings.warn(warn_message, ResourceWarning)962 963 def __repr__(self):964 return "<{} {!r}>".format(self.__class__.__name__, self.name)965 966 def __enter__(self):967 return self.name968 969 def __exit__(self, exc, value, tb):970 if self._delete:971 self.cleanup()972 973 def cleanup(self):974 if self._finalizer.detach() or _os.path.exists(self.name):975 self._rmtree(self.name, ignore_errors=self._ignore_cleanup_errors)976 977 __class_getitem__ = classmethod(_types.GenericAlias)978 