Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/deprecations/pending-removal-in-3.21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
51 changes: 40 additions & 11 deletions Doc/library/tempfile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Comment thread
sobolevn marked this conversation as resolved.

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
Comment thread
sobolevn marked this conversation as resolved.
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)

Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 16 additions & 2 deletions Lib/tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"TMP_MAX", "gettempprefix", # constants
"tempdir", "gettempdir",
"gettempprefixb", "gettempdirb",
"TemporaryFileWrapper",
]


Expand Down Expand Up @@ -484,7 +485,7 @@ def __del__(self):
_warnings.warn(self.warn_message, ResourceWarning)


class _TemporaryFileWrapper:
class TemporaryFileWrapper:
"""Temporary file wrapper

This class provides a wrapper around files opened for
Expand Down Expand Up @@ -555,6 +556,19 @@ def __iter__(self):
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,
Expand Down Expand Up @@ -607,7 +621,7 @@ def opener(*args):
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_unexpected_error is failing because you did not change mock.patch('tempfile._TemporaryFileWrapper') to mock.patch('tempfile.TemporaryFileWrapper') in that function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for pointing out

except:
file.close()
raise
Expand Down
18 changes: 15 additions & 3 deletions Lib/test/test_tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def test_exports(self):
"template" : 1,
"SpooledTemporaryFile" : 1,
"TemporaryDirectory" : 1,
"TemporaryFileWrapper" : 1,
}

unexp = []
Expand Down Expand Up @@ -980,12 +981,23 @@ def do_create(self, dir=None, pre="", suf="", delete=True):

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.
Expand Down Expand Up @@ -1141,7 +1153,7 @@ def my_func(dir):
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()
Expand Down Expand Up @@ -1185,7 +1197,7 @@ def test_bad_encoding(self):
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):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_urllib_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def setUp(self):
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:
Expand Down
2 changes: 1 addition & 1 deletion Lib/urllib/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
__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?
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading