fix issue #6822: ftplib's storline method doesn't work with text files

This commit is contained in:
Giampaolo Rodolà 2010-08-04 10:12:00 +00:00
parent b5c23761d3
commit b939235c6a
4 changed files with 52 additions and 12 deletions

View File

@ -493,9 +493,15 @@ class FTP:
while 1: while 1:
buf = fp.readline() buf = fp.readline()
if not buf: break if not buf: break
if buf[-2:] != B_CRLF: if isinstance(buf, str):
if buf[-1] in B_CRLF: buf = buf[:-1] if not buf.endswith(CRLF):
buf = buf + B_CRLF if buf[-1] in CRLF: buf = buf[:-1]
buf = buf + CRLF
buf = bytes(buf, self.encoding)
else:
if not buf.endswith(B_CRLF):
if buf[-1:] in B_CRLF: buf = buf[:-1]
buf = buf + B_CRLF
conn.sendall(buf) conn.sendall(buf)
if callback: callback(buf) if callback: callback(buf)
conn.close() conn.close()
@ -771,9 +777,15 @@ else:
while 1: while 1:
buf = fp.readline() buf = fp.readline()
if not buf: break if not buf: break
if buf[-2:] != B_CRLF: if isinstance(buf, str):
if buf[-1] in B_CRLF: buf = buf[:-1] if not buf.endswith(CRLF):
buf = buf + B_CRLF if buf[-1] in CRLF: buf = buf[:-1]
buf = buf + CRLF
buf = bytes(buf, self.encoding)
else:
if not buf.endswith(B_CRLF):
if buf[-1:] in B_CRLF: buf = buf[:-1]
buf = buf + B_CRLF
conn.sendall(buf) conn.sendall(buf)
if callback: callback(buf) if callback: callback(buf)
# shutdown ssl layer # shutdown ssl layer
@ -783,6 +795,7 @@ else:
conn.close() conn.close()
return self.voidresp() return self.voidresp()
__all__.append('FTP_TLS') __all__.append('FTP_TLS')
all_errors = (Error, IOError, EOFError, ssl.SSLError) all_errors = (Error, IOError, EOFError, ssl.SSLError)

View File

@ -24,6 +24,7 @@ threading = support.import_module('threading')
# the dummy data returned by server over the data channel when # the dummy data returned by server over the data channel when
# RETR, LIST and NLST commands are issued # RETR, LIST and NLST commands are issued
RETR_DATA = 'abcde12345\r\n' * 1000 RETR_DATA = 'abcde12345\r\n' * 1000
RETR_TEXT = 'abcd\xe912345\r\n' * 1000
LIST_DATA = 'foo\r\nbar\r\n' LIST_DATA = 'foo\r\nbar\r\n'
NLST_DATA = 'foo\r\nbar\r\n' NLST_DATA = 'foo\r\nbar\r\n'
@ -37,7 +38,7 @@ class DummyDTPHandler(asynchat.async_chat):
self.baseclass.last_received_data = '' self.baseclass.last_received_data = ''
def handle_read(self): def handle_read(self):
self.baseclass.last_received_data += self.recv(1024).decode('ascii') self.baseclass.last_received_data += self.recv(1024).decode('latin-1')
def handle_close(self): def handle_close(self):
# XXX: this method can be called many times in a row for a single # XXX: this method can be called many times in a row for a single
@ -49,7 +50,7 @@ class DummyDTPHandler(asynchat.async_chat):
self.dtp_conn_closed = True self.dtp_conn_closed = True
def push(self, what): def push(self, what):
super(DummyDTPHandler, self).push(what.encode('ascii')) super(DummyDTPHandler, self).push(what.encode('latin-1'))
def handle_error(self): def handle_error(self):
raise raise
@ -68,6 +69,7 @@ class DummyFTPHandler(asynchat.async_chat):
self.last_received_data = '' self.last_received_data = ''
self.next_response = '' self.next_response = ''
self.rest = None self.rest = None
self.current_type = 'a'
self.push('220 welcome') self.push('220 welcome')
def collect_incoming_data(self, data): def collect_incoming_data(self, data):
@ -175,7 +177,16 @@ class DummyFTPHandler(asynchat.async_chat):
self.push('257 "pwd ok"') self.push('257 "pwd ok"')
def cmd_type(self, arg): def cmd_type(self, arg):
self.push('200 type ok') # ASCII type
if arg.lower() == 'a':
self.current_type = 'a'
self.push('200 type ok')
# Binary type
elif arg.lower() == 'i':
self.current_type = 'i'
self.push('200 type ok')
else:
self.push('504 unsupported type')
def cmd_quit(self, arg): def cmd_quit(self, arg):
self.push('221 quit ok') self.push('221 quit ok')
@ -194,7 +205,10 @@ class DummyFTPHandler(asynchat.async_chat):
offset = int(self.rest) offset = int(self.rest)
else: else:
offset = 0 offset = 0
self.dtp.push(RETR_DATA[offset:]) if self.current_type == 'i':
self.dtp.push(RETR_DATA[offset:])
else:
self.dtp.push(RETR_TEXT[offset:])
self.dtp.close_when_done() self.dtp.close_when_done()
self.rest = None self.rest = None
@ -511,7 +525,7 @@ class TestFTPClass(TestCase):
def test_retrlines(self): def test_retrlines(self):
received = [] received = []
self.client.retrlines('retr', received.append) self.client.retrlines('retr', received.append)
self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', '')) self.assertEqual(''.join(received), RETR_TEXT.replace('\r\n', ''))
def test_storbinary(self): def test_storbinary(self):
f = io.BytesIO(RETR_DATA.encode('ascii')) f = io.BytesIO(RETR_DATA.encode('ascii'))
@ -530,7 +544,7 @@ class TestFTPClass(TestCase):
self.client.storbinary('stor', f, rest=r) self.client.storbinary('stor', f, rest=r)
self.assertEqual(self.server.handler_instance.rest, str(r)) self.assertEqual(self.server.handler_instance.rest, str(r))
def test_storlines(self): def test_storlines_bytes(self):
f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii')) f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii'))
self.client.storlines('stor', f) self.client.storlines('stor', f)
self.assertEqual(self.server.handler_instance.last_received_data, RETR_DATA) self.assertEqual(self.server.handler_instance.last_received_data, RETR_DATA)
@ -540,6 +554,16 @@ class TestFTPClass(TestCase):
self.client.storlines('stor foo', f, callback=lambda x: flag.append(None)) self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
self.assertTrue(flag) self.assertTrue(flag)
def test_storlines_str(self):
f = io.StringIO(RETR_TEXT.replace('\r\n', '\n'))
self.client.storlines('stor', f)
self.assertEqual(self.server.handler_instance.last_received_data, RETR_TEXT)
# test new callback arg
flag = []
f.seek(0)
self.client.storlines('stor foo', f, callback=lambda x: flag.append(None))
self.assertTrue(flag)
def test_nlst(self): def test_nlst(self):
self.client.nlst() self.client.nlst()
self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1]) self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])

View File

@ -896,3 +896,4 @@ Uwe Zessin
Tarek Ziadé Tarek Ziadé
Peter Åstrand Peter Åstrand
Alexander Shigin Alexander Shigin
Robert DeVaughn

View File

@ -37,6 +37,8 @@ Extensions
Library Library
------- -------
- Issue #6822: ftplib's storlines method doesn't work with text files.
- Issue #2944: asyncore doesn't handle connection refused correctly. - Issue #2944: asyncore doesn't handle connection refused correctly.
- Issue #4184: Private attributes on smtpd.SMTPChannel made public and - Issue #4184: Private attributes on smtpd.SMTPChannel made public and