From 840f7699060e6e3e11273c7c574784c92a92b2ef Mon Sep 17 00:00:00 2001 From: ganesh-k13 Date: Sun, 14 May 2023 11:40:21 +0530 Subject: [PATCH] gh-87646: Added `.path` to tempfile Added `.path` property to generate pathlib's `Path` objects for NamedTemporaryFile and TemporaryDirectory --- Doc/library/tempfile.rst | 21 ++++++++++++++++--- Lib/tempfile.py | 9 ++++++++ Lib/test/test_tempfile.py | 13 ++++++++++++ ...3-05-14-11-46-21.gh-issue-87646.DNJjDX.rst | 3 +++ 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-05-14-11-46-21.gh-issue-87646.DNJjDX.rst diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index fd4c294613fd31..285536a0a9c426 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -90,9 +90,9 @@ The module defines the following user-callable items: attribute is the underlying true file object. This :term:`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. On Unix, unlike with the - :func:`TemporaryFile`, the directory entry does not get unlinked immediately - after the file creation. + and :class:`pathlib.Path` path using :attr:`path` of the returned file-like + object. On Unix, unlike with the :func:`TemporaryFile`, the directory entry + does not get unlinked immediately after the file creation. 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 @@ -142,6 +142,9 @@ The module defines the following user-callable items: .. versionchanged:: 3.12 Added *delete_on_close* parameter. + .. versionchanged:: 3.13 + Added *path* property. + .. class:: SpooledTemporaryFile(max_size=0, mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, *, errors=None) @@ -211,6 +214,9 @@ The module defines the following user-callable items: .. versionchanged:: 3.12 Added the *delete* parameter. + .. versionchanged:: 3.13 + Added *path* property. + .. function:: mkstemp(suffix=None, prefix=None, dir=None, text=False) @@ -421,6 +427,15 @@ Here are some examples of typical usage of the :mod:`tempfile` module:: >>> # directory and contents have been removed + # create a named temporary file, similar to TemporaryFile + # but location and name is accessible + >>> with tempfile.NamedTemporaryFile as fp: + ... print('file name: ', fp.name) + ... print('file path: ', fp.path) + file name: '/tmp/tmpfiehg5my' + file path: PosixPath('/tmp/tmpfiehg5my') + + .. _tempfile-mktemp-deprecated: Deprecated functions and variables diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 2b4f4313247128..3c0caef913176a 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -40,6 +40,7 @@ import warnings as _warnings import io as _io import os as _os +from pathlib import Path as _Path import shutil as _shutil import errno as _errno from random import Random as _Random @@ -518,6 +519,10 @@ def __iter__(self): for line in self.file: yield line + @_functools.cached_property + def path(self): + return _Path(self.name) + def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, delete=True, *, errors=None, @@ -918,6 +923,10 @@ def __exit__(self, exc, value, tb): if self._delete: self.cleanup() + @_functools.cached_property + def path(self): + return _Path(self.name) + def cleanup(self): if self._finalizer.detach() or _os.path.exists(self.name): self._rmtree(self.name, ignore_errors=self._ignore_cleanup_errors) diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index db08fb1c7f2a42..e4eace7b8ba218 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1147,6 +1147,12 @@ def test_unexpected_error(self): mock_open().close.assert_called() self.assertEqual(os.listdir(dir), []) + def test_path_method(self): + d = self.do_create() + path = d.path + self.assertTrue(issubclass(type(path), pathlib.Path), "unexpected return type") + self.assertEqual(str(path), d.name, ".path .name mismatch") + # How to test the mode and bufsize parameters? class TestSpooledTemporaryFile(BaseTestCase): @@ -1853,5 +1859,12 @@ def test_delete_false(self): self.assertTrue(os.path.exists(working_dir)) shutil.rmtree(working_dir) + def test_path_method(self): + d = self.do_create() + path = d.path + self.assertTrue(issubclass(type(path), pathlib.Path), "unexpected return type") + self.assertEqual(str(path), d.name, ".path .name mismatch") + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2023-05-14-11-46-21.gh-issue-87646.DNJjDX.rst b/Misc/NEWS.d/next/Library/2023-05-14-11-46-21.gh-issue-87646.DNJjDX.rst new file mode 100644 index 00000000000000..1cbc0898c6e6e6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-14-11-46-21.gh-issue-87646.DNJjDX.rst @@ -0,0 +1,3 @@ +:class:`tempfile.NamedTemporaryFile` and :class:`tempfile.TemporaryDirectory` +includes a new ``.path`` property. This provides the temporary file name as a +:class:`pathlib.Path` object