mirror of
https://github.com/python/cpython.git
synced 2026-07-25 11:22:45 -04:00
gh-152586: Make tempfile.TemporaryFileWrapper public (#152646)
Deprecate old compatibility `tempfile._TemporaryFileWrapper` name, schedule it to be removed in Python 3.21 Co-authored-by: sobolevn <mail@sobolevn.me> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
This commit is contained in:
@@ -23,3 +23,8 @@ Pending removal in Python 3.21
|
||||
* Soft-deprecated since Python 3.15, using ``'F'`` and ``'D'`` type codes are now
|
||||
deprecated. These codes will be removed in Python 3.21. Use instead
|
||||
two-letter forms ``'Zf'`` and ``'Zd'``.
|
||||
|
||||
* :mod:`tempfile`:
|
||||
|
||||
* ``tempfile._TemporaryFileWrapper`` will be removed in Python 3.21. Use the
|
||||
public :class:`tempfile.TemporaryFileWrapper` instead.
|
||||
|
||||
+40
-11
@@ -14,10 +14,11 @@
|
||||
|
||||
This module creates temporary files and directories. It works on all
|
||||
supported platforms. :class:`TemporaryFile`, :class:`NamedTemporaryFile`,
|
||||
:class:`TemporaryDirectory`, and :class:`SpooledTemporaryFile` are high-level
|
||||
interfaces which provide automatic cleanup and can be used as
|
||||
:term:`context managers <context manager>`. :func:`mkstemp` and
|
||||
:func:`mkdtemp` are lower-level functions which require manual cleanup.
|
||||
:class:`TemporaryFileWrapper`, :class:`TemporaryDirectory`, and
|
||||
:class:`SpooledTemporaryFile` are high-level interfaces which provide
|
||||
automatic cleanup and can be used as :term:`context managers
|
||||
<context manager>`. :func:`mkstemp` and :func:`mkdtemp` are lower-level
|
||||
functions which require manual cleanup.
|
||||
|
||||
All the user-callable functions and constructors take additional arguments which
|
||||
allow direct control over the location and name of temporary files and
|
||||
@@ -84,13 +85,13 @@ The module defines the following user-callable items:
|
||||
:func:`TemporaryFile` with *delete* and *delete_on_close* parameters that
|
||||
determine whether and how the named file should be automatically deleted.
|
||||
|
||||
The returned object is always a :term:`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. 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.
|
||||
The returned object is always a :class:`TemporaryFileWrapper` instance
|
||||
(a :term:`file-like object`) whose :attr:`~TemporaryFileWrapper.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.
|
||||
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
|
||||
@@ -140,6 +141,34 @@ The module defines the following user-callable items:
|
||||
.. versionchanged:: 3.12
|
||||
Added *delete_on_close* parameter.
|
||||
|
||||
.. class:: TemporaryFileWrapper(file, name, delete=True, delete_on_close=True)
|
||||
|
||||
A mutable wrapper returned by :func:`NamedTemporaryFile`. It wraps the
|
||||
underlying file object, delegating attribute access to it, and ensures
|
||||
the temporary file is deleted when appropriate.
|
||||
|
||||
.. attribute:: file
|
||||
|
||||
The underlying :term:`file-like object`.
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
The file name of the temporary file.
|
||||
|
||||
.. method:: close()
|
||||
|
||||
Close the temporary file, possibly deleting it depending on the
|
||||
*delete* and *delete_on_close* arguments passed to
|
||||
:func:`NamedTemporaryFile`.
|
||||
|
||||
.. note::
|
||||
|
||||
``tempfile._TemporaryFileWrapper`` is kept as a backwards compatible
|
||||
deprecated alias for this class.
|
||||
It will be removed in Python 3.21
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
|
||||
.. class:: SpooledTemporaryFile(max_size=0, mode='w+b', buffering=-1, encoding=None, newline=None, suffix=None, prefix=None, dir=None, *, errors=None)
|
||||
|
||||
|
||||
@@ -588,6 +588,13 @@ New deprecations
|
||||
two-letter forms ``'Zf'`` and ``'Zd'``.
|
||||
(Contributed by Sergey B Kirpichev in :gh:`121249`.)
|
||||
|
||||
* :mod:`tempfile`
|
||||
|
||||
* The private ``tempfile._TemporaryFileWrapper`` name is deprecated
|
||||
and is slated for removal in Python 3.21.
|
||||
Use the new public :class:`tempfile.TemporaryFileWrapper` instead,
|
||||
which is the return type of :func:`tempfile.NamedTemporaryFile`.
|
||||
|
||||
.. Add deprecations above alphabetically, not here at the end.
|
||||
|
||||
.. include:: ../deprecations/pending-removal-in-3.17.rst
|
||||
|
||||
+16
-2
@@ -31,6 +31,7 @@ __all__ = [
|
||||
"TMP_MAX", "gettempprefix", # constants
|
||||
"tempdir", "gettempdir",
|
||||
"gettempprefixb", "gettempdirb",
|
||||
"TemporaryFileWrapper",
|
||||
]
|
||||
|
||||
|
||||
@@ -484,7 +485,7 @@ class _TemporaryFileCloser:
|
||||
_warnings.warn(self.warn_message, ResourceWarning)
|
||||
|
||||
|
||||
class _TemporaryFileWrapper:
|
||||
class TemporaryFileWrapper:
|
||||
"""Temporary file wrapper
|
||||
|
||||
This class provides a wrapper around files opened for
|
||||
@@ -555,6 +556,19 @@ class _TemporaryFileWrapper:
|
||||
for line in self.file:
|
||||
yield line
|
||||
|
||||
def __getattr__(name):
|
||||
if name == "_TemporaryFileWrapper":
|
||||
_warnings._deprecated(
|
||||
"tempfile._TemporaryFileWrapper",
|
||||
message=(
|
||||
"{name!r} is deprecated and slated for removal in Python {remove}. "
|
||||
"Use tempfile.TemporaryFileWrapper instead."
|
||||
),
|
||||
remove=(3, 21),
|
||||
)
|
||||
return TemporaryFileWrapper
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None,
|
||||
newline=None, suffix=None, prefix=None,
|
||||
dir=None, delete=True, *, errors=None,
|
||||
@@ -607,7 +621,7 @@ def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None,
|
||||
raw = getattr(file, 'buffer', file)
|
||||
raw = getattr(raw, 'raw', raw)
|
||||
raw.name = name
|
||||
return _TemporaryFileWrapper(file, name, delete, delete_on_close)
|
||||
return TemporaryFileWrapper(file, name, delete, delete_on_close)
|
||||
except:
|
||||
file.close()
|
||||
raise
|
||||
|
||||
@@ -148,6 +148,7 @@ class TestExports(BaseTestCase):
|
||||
"template" : 1,
|
||||
"SpooledTemporaryFile" : 1,
|
||||
"TemporaryDirectory" : 1,
|
||||
"TemporaryFileWrapper" : 1,
|
||||
}
|
||||
|
||||
unexp = []
|
||||
@@ -980,12 +981,23 @@ class TestNamedTemporaryFile(BaseTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
# NamedTemporaryFile can create files
|
||||
self.do_create()
|
||||
f = self.do_create()
|
||||
self.assertIsInstance(f, tempfile.TemporaryFileWrapper)
|
||||
self.do_create(pre="a")
|
||||
self.do_create(suf="b")
|
||||
self.do_create(pre="a", suf="b")
|
||||
self.do_create(pre="aa", suf=".txt")
|
||||
|
||||
def test_in_all(self):
|
||||
self.assertIn("TemporaryFileWrapper", tempfile.__all__)
|
||||
|
||||
def test_deprecated_TemporaryFileWrapper_alias(self):
|
||||
# gh-152586: _TemporaryFileWrapper is a deprecated alias
|
||||
# for the public TemporaryFileWrapper class.
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
obj = tempfile._TemporaryFileWrapper
|
||||
self.assertIs(obj, tempfile.TemporaryFileWrapper)
|
||||
|
||||
def test_method_lookup(self):
|
||||
# Issue #18879: Looking up a temporary file method should keep it
|
||||
# alive long enough.
|
||||
@@ -1141,7 +1153,7 @@ class TestNamedTemporaryFile(BaseTestCase):
|
||||
try:
|
||||
with self.assertWarnsRegex(
|
||||
expected_warning=ResourceWarning,
|
||||
expected_regex=r"Implicitly cleaning up <_TemporaryFileWrapper file=.*>",
|
||||
expected_regex=r"Implicitly cleaning up <TemporaryFileWrapper file=.*>",
|
||||
):
|
||||
tmp_name = my_func(dir)
|
||||
support.gc_collect()
|
||||
@@ -1185,7 +1197,7 @@ class TestNamedTemporaryFile(BaseTestCase):
|
||||
def test_unexpected_error(self):
|
||||
dir = tempfile.mkdtemp()
|
||||
self.addCleanup(os_helper.rmtree, dir)
|
||||
with mock.patch('tempfile._TemporaryFileWrapper') as mock_ntf, \
|
||||
with mock.patch('tempfile.TemporaryFileWrapper') as mock_ntf, \
|
||||
mock.patch('io.open', mock.mock_open()) as mock_open:
|
||||
mock_ntf.side_effect = KeyboardInterrupt()
|
||||
with self.assertRaises(KeyboardInterrupt):
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestResponse(unittest.TestCase):
|
||||
def test_with(self):
|
||||
addbase = urllib.response.addbase(self.fp)
|
||||
|
||||
self.assertIsInstance(addbase, tempfile._TemporaryFileWrapper)
|
||||
self.assertIsInstance(addbase, tempfile.TemporaryFileWrapper)
|
||||
|
||||
def f():
|
||||
with addbase as spam:
|
||||
|
||||
@@ -11,7 +11,7 @@ import tempfile
|
||||
__all__ = ['addbase', 'addclosehook', 'addinfo', 'addinfourl']
|
||||
|
||||
|
||||
class addbase(tempfile._TemporaryFileWrapper):
|
||||
class addbase(tempfile.TemporaryFileWrapper):
|
||||
"""Base class for addinfo and addclosehook. Is a good idea for garbage collection."""
|
||||
|
||||
# XXX Add a method to expose the timeout on the underlying socket?
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
:class:`!tempfile._TemporaryFileWrapper` has been renamed to the public :class:`tempfile.TemporaryFileWrapper`. The old private name is kept as a deprecated alias and will be removed in Python 3.21.
|
||||
Reference in New Issue
Block a user