diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst index f0a81a093b435b..f2d2f2498f9c3f 100644 --- a/Doc/library/tempfile.rst +++ b/Doc/library/tempfile.rst @@ -142,6 +142,9 @@ The module defines the following user-callable items: .. versionchanged:: 3.12 Added *delete_on_close* parameter. + .. versionchanged:: next + Added support for the :term:`path-like object` protocol. + .. class:: SpooledTemporaryFile(max_size=0, mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, *, errors=None) @@ -217,6 +220,9 @@ The module defines the following user-callable items: .. versionchanged:: 3.12 Added the *delete* parameter. + .. versionchanged:: next + Added support for the :term:`path-like object` protocol. + .. function:: mkstemp(suffix=None, prefix=None, dir=None, text=False) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 5b03bd9e5a8caf..22b5010e99e061 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1218,6 +1218,14 @@ sysconfig (Contributed by Xuehai Pan in :gh:`131799`.) +tempfile +-------- + +* :func:`tempfile.NamedTemporaryFile` and :func:`~tempfile.TemporaryDirectory` + now return a :term:`path-like object`. + (Contributed by Barney Gale in :gh:`87646`.) + + threading --------- diff --git a/Lib/tempfile.py b/Lib/tempfile.py index cadb0bed3cce3b..b1e6dfe786025b 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -525,6 +525,10 @@ def func_wrapper(*args, **kwargs): setattr(self, name, a) return a + def __fspath__(self): + """Return the filesystem path of the temporary file.""" + return self.name + # The underlying __enter__ method returns the wrong object # (self.file) so override it to return the wrapper def __enter__(self): @@ -962,6 +966,10 @@ def _cleanup(cls, name, warn_message, ignore_errors=False, delete=True): def __repr__(self): return "<{} {!r}>".format(self.__class__.__name__, self.name) + def __fspath__(self): + """Return the filesystem path of the temporary directory.""" + return self.name + def __enter__(self): return self.name diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index d46d3c0f040601..229d17fe6fb160 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -950,8 +950,12 @@ def do_create(self, dir=None, pre="", suf="", delete=True): delete=delete) self.nameCheck(file.name, dir, pre, suf) + self.nameCheck(os.fspath(file), dir, pre, suf) return file + def test_pathlike(self): + tmp = self.do_create() + self.assertIsInstance(tmp, os.PathLike) def test_basic(self): # NamedTemporaryFile can create files @@ -1623,9 +1627,14 @@ def do_create(self, dir=None, pre="", suf="", recurse=1, dirs=1, files=1, dir=dir, prefix=pre, suffix=suf, ignore_cleanup_errors=ignore_cleanup_errors) self.nameCheck(tmp.name, dir, pre, suf) + self.nameCheck(os.fspath(tmp), dir, pre, suf) self.do_create2(tmp.name, recurse, dirs, files) return tmp + def test_pathlike(self): + tmp = self.do_create() + self.assertIsInstance(tmp, os.PathLike) + def do_create2(self, path, recurse=1, dirs=1, files=1): # Create subdirectories and some files if recurse: diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index b7840d0f945a66..8a6d162de817fe 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -1377,12 +1377,17 @@ def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True, "metadata_encoding is only supported for reading files") # Check if we were passed a file-like object - if isinstance(file, os.PathLike): - file = os.fspath(file) - if isinstance(file, str): + if hasattr(file, 'read') or hasattr(file, 'write'): + self._filePassed = 1 + self.fp = file + if isinstance(file, os.PathLike): + self.filename = os.fspath(file) + else: + self.filename = getattr(file, 'name', None) + else: # No, it's a filename self._filePassed = 0 - self.filename = file + self.filename = os.fspath(file) modeDict = {'r' : 'rb', 'w': 'w+b', 'x': 'x+b', 'a' : 'r+b', 'r+b': 'w+b', 'w+b': 'wb', 'x+b': 'xb'} filemode = modeDict[mode] @@ -1395,10 +1400,6 @@ def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True, continue raise break - else: - self._filePassed = 1 - self.fp = file - self.filename = getattr(file, 'name', None) self._fileRefCnt = 1 self._lock = threading.RLock() self._seekable = True diff --git a/Misc/NEWS.d/next/Library/2024-01-30-21-07-20.gh-issue-87646.TQUZMn.rst b/Misc/NEWS.d/next/Library/2024-01-30-21-07-20.gh-issue-87646.TQUZMn.rst new file mode 100644 index 00000000000000..18272be258e022 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-01-30-21-07-20.gh-issue-87646.TQUZMn.rst @@ -0,0 +1,3 @@ +Return a :term:`path-like object` from :func:`tempfile.NamedTemporaryFile` +and :func:`~tempfile.TemporaryDirectory`. (Contributed by Barney Gale in +:gh:`87646`.)