Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
bpo-14243: Optionally delete NamedTemporaryFile on content manager exit but not on file close#22431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
bpo-14243: Optionally delete NamedTemporaryFile on content manager exit but not on file close #22431
Changes from all commits
144db7f21f7df02d7927b4fcddaf76f28f36be9be6faf1a34589d0b8e22a3db3be2f4739e8b504d8f1529ec55f5d6f382ef9dc427e29b7d2d003a39e2fc6db3ac4e20653cf27d5d07f40b32b285773e71d98f2709598e6b5a7e1777e7ad854d5677ecf5b1b57fd339ada71a965e22c9cb1f515207076e8a0a8ae3File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -75,20 +75,50 @@ The module defines the following user-callable items: | ||
| Added *errors* parameter. | ||
| .. function:: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, *, errors=None) | ||
| This function operates exactly as :func:`TemporaryFile` does, except that | ||
| the file is guaranteed to have a visible name in the file system (on | ||
| Unix, the directory entry is not unlinked). That name can be retrieved | ||
| from the :attr:`name` attribute of the returned | ||
| file-like object. Whether the name can be | ||
| used to open the file a second time, while the named temporary file is | ||
| still open, varies across platforms (it can be so used on Unix; it cannot | ||
| on Windows). If *delete* is true (the default), the file is | ||
| deleted as soon as it is closed. | ||
| .. function:: NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, *, errors=None, delete_on_close=True) | ||
| This function operates exactly as :func:`TemporaryFile` does, except the | ||
| following differences: | ||
| * The file is guaranteed to have a visible name in the file system (on Unix, | ||
| the directory entry is not unlinked). | ||
| * There is more granularity in the deletion behaviour of the file (see | ||
| ``delete_on_close`` below) | ||
| The returned object is always a file-like object whose :attr:`!file` | ||
| attribute is the underlying true file object. This file-like object can | ||
| be used in a :keyword:`with` statement, just like a normal file. | ||
| attribute is the underlying true file object. This file-like object can be | ||
| used in a :keyword:`with` statement, just like a normal file. The name of the | ||
| temporary file can be retrieved from the :attr:`name` attribute of the | ||
| returned file-like object. | ||
| If *delete* is true (the default) and *delete_on_close* is true (the | ||
| default), the file is deleted as soon as it is closed. If *delete* is true | ||
| and *delete_on_close* is false, the file is deleted on context manager exit | ||
| only. If *delete* is false, the value of *delete_on_close* is ignored. | ||
| While the named temporary file is open, the file can always be opened again | ||
| on POSIX. On Windows, it can be opened again if *delete* is false, or if | ||
| *delete_on_close* is false, or if the additional open shares delete access | ||
| (e.g. by calling :func:`os.open` with the flag ``O_TEMPORARY``). On | ||
| Windows, if *delete* is true and *delete_on_close* is false, additional | ||
| opens that do not share delete access (e.g. via builtin :func:`open`) must | ||
| be closed before exiting the context manager, else the :func:`os.unlink` | ||
| call on context manager exit will fail with a :exc:`PermissionError`. | ||
| To use the name of the temporary file to open the closed file second time, | ||
| either make sure not to delete the file upon closure (set the *delete* | ||
| parameter to be false) or, in case the temporary file is created in a | ||
| :keyword:`with` statement, set the *delete_on_close* parameter to be false. | ||
| The latter approach is recommended as it provides assistance in automatic | ||
| cleaning of the temporary file upon the context manager exit. | ||
Comment on lines
+109
to
+114
| ||
| On Windows, if *delete_on_close* is false, and the file is created in a | ||
| directory for which the user lacks delete access, then the :func:`os.unlink` | ||
| call on exit of the context manager will fail with a :exc:`PermissionError`. | ||
| This cannot happen when *delete_on_close* is true because delete access is | ||
| requested by the open, which fails immediately if the requested access is not | ||
| granted. | ||
| On POSIX (only), a process that is terminated abruptly with SIGKILL | ||
| cannot automatically delete any NamedTemporaryFiles it created. | ||
| @@ -98,6 +128,9 @@ The module defines the following user-callable items: | ||
| .. versionchanged:: 3.8 | ||
| Added *errors* parameter. | ||
| .. versionchanged:: 3.12 | ||
| Added *delete_on_close* parameter. | ||
| .. class:: SpooledTemporaryFile(max_size=0, mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, *, errors=None) | ||
| @@ -346,6 +379,22 @@ Here are some examples of typical usage of the :mod:`tempfile` module:: | ||
| >>> | ||
| # file is now closed and removed | ||
| # create a temporary file using a context manager, note the name, | ||
| # close the file, use the name to open the file again | ||
| >>> with tempfile.TemporaryFile(delete_on_close=False) as fp: | ||
| ... fp.write(b'Hello world!') | ||
| ... fp_name = fp.name | ||
| ... fp.close() | ||
| # the file is closed, but not removed | ||
| # open the file again by using its name | ||
| ... f = open(fp_name) | ||
| ... f.seek(0) | ||
| ... f.read() | ||
| b'Hello world!' | ||
| ... f.close() | ||
| >>> | ||
| # file is now removed | ||
| # create a temporary directory using the context manager | ||
| >>> with tempfile.TemporaryDirectory() as tmpdirname: | ||
| ... print('created temporary directory', tmpdirname) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -421,10 +421,11 @@ class _TemporaryFileCloser: | ||
| file = None # Set here since __del__ checks it | ||
| close_called = False | ||
| def __init__(self, file, name, delete=True): | ||
| def __init__(self, file, name, delete=True, delete_on_close=True): | ||
| self.file = file | ||
| self.name = name | ||
| self.delete = delete | ||
| self.delete_on_close = delete_on_close | ||
| # NT provides delete-on-close as a primitive, so we don't need | ||
| # the wrapper to do anything special. We still use it so that | ||
| @@ -442,7 +443,7 @@ def close(self, unlink=_os.unlink): | ||
| try: | ||
| self.file.close() | ||
| finally: | ||
| if self.delete: | ||
| if self.delete and self.delete_on_close: | ||
| unlink(self.name) | ||
| # Need to ensure the file is deleted on __del__ | ||
| @@ -464,11 +465,13 @@ class _TemporaryFileWrapper: | ||
| remove the file when it is no longer needed. | ||
| """ | ||
| def __init__(self, file, name, delete=True): | ||
| def __init__(self, file, name, delete=True, delete_on_close=True): | ||
| self.file = file | ||
| self.name = name | ||
| self.delete = delete | ||
| self._closer = _TemporaryFileCloser(file, name, delete) | ||
| self.delete_on_close = delete_on_close | ||
| self._closer = _TemporaryFileCloser(file, name, delete, | ||
| delete_on_close) | ||
| def __getattr__(self, name): | ||
| # Attribute lookups are delegated to the underlying file | ||
| @@ -500,6 +503,15 @@ def __enter__(self): | ||
| def __exit__(self, exc, value, tb): | ||
| result = self.file.__exit__(exc, value, tb) | ||
| self.close() | ||
| # If the file is to be deleted, but not on close, delete it now. | ||
| if self.delete and not self.delete_on_close: | ||
| try: | ||
| _os.unlink(self.name) | ||
| # It is okay to ignore FileNotFoundError. The file may have | ||
| # been deleted already. | ||
| except FileNotFoundError: | ||
| pass | ||
Comment on lines
+506
to
+514
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A case hasn't been covered. If
| ||
| return result | ||
| def close(self): | ||
| @@ -521,15 +533,18 @@ def __iter__(self): | ||
| def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, | ||
| newline=None, suffix=None, prefix=None, | ||
| dir=None, delete=True, *, errors=None): | ||
| dir=None, delete=True, *, errors=None, | ||
| delete_on_close=True): | ||
| """Create and return a temporary file. | ||
| Arguments: | ||
| 'prefix', 'suffix', 'dir' -- as for mkstemp. | ||
| 'mode' -- the mode argument to io.open (default "w+b"). | ||
| 'buffering' -- the buffer size argument to io.open (default -1). | ||
| 'encoding' -- the encoding argument to io.open (default None) | ||
| 'newline' -- the newline argument to io.open (default None) | ||
| 'delete' -- whether the file is deleted on close (default True). | ||
| 'delete' -- whether the file is automatically deleted (default True). | ||
| 'delete_on_close' -- if 'delete', whether the file is deleted on close | ||
| or on context exit (default True). | ||
| 'errors' -- the errors argument to io.open (default None) | ||
| The file is created as mkstemp() would do it. | ||
| @@ -548,7 +563,7 @@ def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, | ||
| # Setting O_TEMPORARY in the flags causes the OS to delete | ||
| # the file when it is closed. This is only supported by Windows. | ||
| if _os.name == 'nt' and delete: | ||
| if _os.name == 'nt' and delete and delete_on_close: | ||
| flags |= _os.O_TEMPORARY | ||
| if "b" not in mode: | ||
| @@ -559,7 +574,7 @@ def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, | ||
| file = _io.open(fd, mode, buffering=buffering, | ||
| newline=newline, encoding=encoding, errors=errors) | ||
| return _TemporaryFileWrapper(file, name, delete) | ||
| return _TemporaryFileWrapper(file, name, delete, delete_on_close) | ||
| except BaseException: | ||
| _os.unlink(name) | ||
| _os.close(fd) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| The :class:`tempfile.NamedTemporaryFile` has a new optional parameter *delete_on_close* |
Uh oh!
There was an error while loading. Please reload this page.