Skip to content
Open
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
44 changes: 25 additions & 19 deletions Lib/imaplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,25 +316,30 @@ def open(self, host='', port=IMAP4_PORT, timeout=None):
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

return self._imaplib_file

@file.setter
def file(self, value):
# 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."""
Expand Down Expand Up @@ -420,7 +425,7 @@ def send(self, data):

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:
Expand Down Expand Up @@ -924,9 +929,10 @@ def starttls(self, ssl_context=None):
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:
Expand Down Expand Up @@ -1681,7 +1687,7 @@ def open(self, host=None, port=None, timeout=None):
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,
Expand Down
23 changes: 19 additions & 4 deletions Lib/test/test_imaplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,11 +665,26 @@ def test_unselect(self):

# 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
self.assertIsInstance(client.file.raw, socket.SocketIO)

def test_file_property_setter(self):
client, _ = self._setup(SimpleIMAPHandler)
# ensure that the caller closes the existing file
client.file.close()
for new_file in [mock.Mock(), None]:
client.file = new_file
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()
self.assertIs(client.file, f)
client.file = None
self.assertIsNone(client.file)
f.close.assert_not_called()


class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:mod:`imaplib`: restore legacy support for altering ``IMAP4.file``. Patch by
Bénédikt Tran.
Loading