* :meth:`http.cookies.BaseCookie.js_output` is deprecated and will be
removed in Python 3.19.
+* :mod:`imaplib`:
+
+ * Altering :attr:`IMAP4.file <imaplib.IMAP4.file>` is now deprecated
+ and slated for removal in Python 3.19. This property is now unused
+ and changing its value does not automatically close the current file.
+
+ Before Python 3.14, this property was used to implement the corresponding
+ ``read()`` and ``readline()`` methods for :class:`~imaplib.IMAP4` but this
+ is no longer the case since then.
.. versionadded:: 3.5
+.. property:: IMAP4.file
+
+ Internal :class:`~io.BufferedReader` associated with the underlying socket.
+ This property is documented for legacy purposes but not part of the public
+ interface. The caller is responsible to ensure that the current file is
+ closed before changing it.
+
+ .. deprecated-removed:: next 3.19
+
+
.. _imap4-example:
IMAP4 Example
(Contributed by kishorhange111 in :gh:`148849`.)
+* :mod:`imaplib`:
+
+ * Altering :attr:`IMAP4.file <imaplib.IMAP4.file>` is now deprecated
+ and slated for removal in Python 3.19. This property is now unused
+ and changing its value does *not* explicitly close the current file.
+
+
* :mod:`re`:
* :func:`re.match` and :meth:`re.Pattern.match` are now
self.host = host
self.port = port
self.sock = self._create_socket(timeout)
- self._file = self.sock.makefile('rb')
-
+ # Since IMAP4 implements its own read() and readline() buffering,
+ # the '_imaplib_file' attribute is unused. Nonetheless it is kept
+ # and exposed solely for backward compatibility purposes.
+ self._imaplib_file = self.sock.makefile('rb')
@property
def file(self):
- # The old 'file' attribute is no longer used now that we do our own
- # read() and readline() buffering, with which it conflicts.
- # As an undocumented interface, it should never have been accessed by
- # external code, and therefore does not warrant deprecation.
- # Nevertheless, we provide this property for now, to avoid suddenly
- # breaking any code in the wild that might have been using it in a
- # harmless way.
import warnings
- warnings.warn(
- 'IMAP4.file is unsupported, can cause errors, and may be removed.',
- RuntimeWarning,
- stacklevel=2)
- return self._file
+ warnings._deprecated("IMAP4.file", remove=(3, 19))
+ return self._imaplib_file
+ @file.setter
+ def file(self, value):
+ import warnings
+ warnings._deprecated("IMAP4.file", remove=(3, 19))
+ # Ideally, we would want to close the previous file,
+ # but since we do not know how subclasses will use
+ # that setter, it is probably better to leave it to
+ # the caller.
+ self._imaplib_file = value
+
+ def _close_imaplib_file(self):
+ file = self._imaplib_file
+ if file is not None:
+ try:
+ file.close()
+ except OSError:
+ pass
def read(self, size):
"""Read 'size' bytes from remote."""
def shutdown(self):
"""Close I/O established in "open"."""
- self._file.close()
+ self._close_imaplib_file()
try:
self.sock.shutdown(socket.SHUT_RDWR)
except OSError as exc:
ssl_context = ssl._create_stdlib_context()
typ, dat = self._simple_command(name)
if typ == 'OK':
+ self._close_imaplib_file()
self.sock = ssl_context.wrap_socket(self.sock,
server_hostname=self.host)
- self._file = self.sock.makefile('rb')
+ self._imaplib_file = self.sock.makefile('rb')
self._tls_established = True
self._get_capabilities()
else:
self.host = None # For compatibility with parent class
self.port = None
self.sock = None
- self._file = None
+ self._imaplib_file = None
self.process = subprocess.Popen(self.command,
bufsize=DEFAULT_BUFFER_SIZE,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
# property tests
- def test_file_property_should_not_be_accessed(self):
+ def test_file_property_getter(self):
client, _ = self._setup(SimpleIMAPHandler)
- # the 'file' property replaced a private attribute that is now unsafe
- with self.assertWarns(RuntimeWarning):
- client.file
+ with self.assertWarns(DeprecationWarning):
+ self.assertIsInstance(client.file.raw, socket.SocketIO)
+
+ def test_file_property_setter(self):
+ client, _ = self._setup(SimpleIMAPHandler)
+ with self.assertWarns(DeprecationWarning):
+ # ensure that the caller closes the existing file
+ client.file.close()
+ for new_file in [mock.Mock(), None]:
+ with self.assertWarns(DeprecationWarning):
+ client.file = new_file
+ with self.assertWarns(DeprecationWarning):
+ self.assertIs(client.file, new_file)
+
+ def test_file_property_setter_should_not_close_previous_file(self):
+ client, _ = self._setup(SimpleIMAPHandler)
+ with mock.patch.object(client, "_imaplib_file", mock.Mock()) as f:
+ f.close.assert_not_called()
+ with self.assertWarns(DeprecationWarning):
+ self.assertIs(client.file, f)
+ with self.assertWarns(DeprecationWarning):
+ client.file = None
+ with self.assertWarns(DeprecationWarning):
+ self.assertIsNone(client.file)
+ f.close.assert_not_called()
class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase):
--- /dev/null
+:mod:`imaplib`: deprecate support for :attr:`IMAP4.file <imaplib.IMAP4.file>`.
+This attribute was never meant to be part of the public interface and altering
+its value may result in unclosed files or other synchronization issues with
+the underlying socket. Patch by Bénédikt Tran.