]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-100131: Add optional delete parameter to tempfile.TemporaryDirectory() (#100132)
authorJakobDev <jakobdev@gmx.de>
Fri, 24 Mar 2023 21:52:06 +0000 (22:52 +0100)
committerGitHub <noreply@github.com>
Fri, 24 Mar 2023 21:52:06 +0000 (14:52 -0700)
Add optional delete parameter to tempfile.TemporaryDirectory().

Co-authored-by: Gregory P. Smith <greg@krypto.org>
Doc/library/tempfile.rst
Lib/tempfile.py
Lib/test/test_tempfile.py
Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst [new file with mode: 0644]

index b6d4f5dd05bbfcc98d444e03cd278ce5936734d7..61358eb76925b23db79f44aecf70fecfb21d67b8 100644 (file)
@@ -173,7 +173,7 @@ The module defines the following user-callable items:
       or text *mode* was specified).
 
 
-.. class:: TemporaryDirectory(suffix=None, prefix=None, dir=None, ignore_cleanup_errors=False)
+.. class:: TemporaryDirectory(suffix=None, prefix=None, dir=None, ignore_cleanup_errors=False, *, delete=True)
 
    This class securely creates a temporary directory using the same rules as :func:`mkdtemp`.
    The resulting object can be used as a context manager (see
@@ -195,6 +195,12 @@ The module defines the following user-callable items:
    (the :func:`cleanup` call, exiting the context manager, when the object
    is garbage-collected or during interpreter shutdown).
 
+   The *delete* parameter can be used to disable cleanup of the directory tree
+   upon exiting the context.  While it may seem unusual for a context manager
+   to disable the action taken when exiting the context, it can be useful during
+   debugging or when you need your cleanup behavior to be conditional based on
+   other logic.
+
    .. audit-event:: tempfile.mkdtemp fullpath tempfile.TemporaryDirectory
 
    .. versionadded:: 3.2
@@ -202,6 +208,9 @@ The module defines the following user-callable items:
    .. versionchanged:: 3.10
       Added *ignore_cleanup_errors* parameter.
 
+   .. versionchanged:: 3.12
+      Added the *delete* parameter.
+
 
 .. function:: mkstemp(suffix=None, prefix=None, dir=None, text=False)
 
index cf06092f826bcc352436eedb3da4d43e2337dc7b..4732eb0efe1f76d4e12d9f5dd0270576dda429bc 100644 (file)
@@ -850,17 +850,26 @@ class TemporaryDirectory:
             ...
 
     Upon exiting the context, the directory and everything contained
-    in it are removed.
+    in it are removed (unless delete=False is passed or an exception
+    is raised during cleanup and ignore_cleanup_errors is not True).
+
+    Optional Arguments:
+        suffix - A str suffix for the directory name.  (see mkdtemp)
+        prefix - A str prefix for the directory name.  (see mkdtemp)
+        dir - A directory to create this temp dir in.  (see mkdtemp)
+        ignore_cleanup_errors - False; ignore exceptions during cleanup?
+        delete - True; whether the directory is automatically deleted.
     """
 
     def __init__(self, suffix=None, prefix=None, dir=None,
-                 ignore_cleanup_errors=False):
+                 ignore_cleanup_errors=False, *, delete=True):
         self.name = mkdtemp(suffix, prefix, dir)
         self._ignore_cleanup_errors = ignore_cleanup_errors
+        self._delete = delete
         self._finalizer = _weakref.finalize(
             self, self._cleanup, self.name,
             warn_message="Implicitly cleaning up {!r}".format(self),
-            ignore_errors=self._ignore_cleanup_errors)
+            ignore_errors=self._ignore_cleanup_errors, delete=self._delete)
 
     @classmethod
     def _rmtree(cls, name, ignore_errors=False):
@@ -894,9 +903,10 @@ class TemporaryDirectory:
         _shutil.rmtree(name, onexc=onexc)
 
     @classmethod
-    def _cleanup(cls, name, warn_message, ignore_errors=False):
-        cls._rmtree(name, ignore_errors=ignore_errors)
-        _warnings.warn(warn_message, ResourceWarning)
+    def _cleanup(cls, name, warn_message, ignore_errors=False, delete=True):
+        if delete:
+            cls._rmtree(name, ignore_errors=ignore_errors)
+            _warnings.warn(warn_message, ResourceWarning)
 
     def __repr__(self):
         return "<{} {!r}>".format(self.__class__.__name__, self.name)
@@ -905,7 +915,8 @@ class TemporaryDirectory:
         return self.name
 
     def __exit__(self, exc, value, tb):
-        self.cleanup()
+        if self._delete:
+            self.cleanup()
 
     def cleanup(self):
         if self._finalizer.detach() or _os.path.exists(self.name):
index 7c2c8de7a2e6fc7cdb4a60d65322fe8c081fd225..90155487cff5859f3e68f0135c81ce9d8d77cf03 100644 (file)
@@ -12,6 +12,7 @@ import stat
 import types
 import weakref
 import gc
+import shutil
 from unittest import mock
 
 import unittest
@@ -1837,6 +1838,11 @@ class TestTemporaryDirectory(BaseTestCase):
             d.cleanup()
         self.assertFalse(os.path.exists(d.name))
 
+    def test_delete_false(self):
+        with tempfile.TemporaryDirectory(delete=False) as working_dir:
+            pass
+        self.assertTrue(os.path.exists(working_dir))
+        shutil.rmtree(working_dir)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst b/Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst
new file mode 100644 (file)
index 0000000..07891f2
--- /dev/null
@@ -0,0 +1 @@
+Added an optional ``delete`` keyword argument to :class:`tempfile.TemporaryDirectory`.