Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b0ec8ee
Updated bdb.py + test_bdb.py
terryluan12 Dec 30, 2025
91d4447
Deleted _pycodecs.py
terryluan12 Dec 30, 2025
ac9bd54
Updated code.py library
terryluan12 Dec 30, 2025
d5f6198
Updated the _pydatetime.py lib
terryluan12 Dec 30, 2025
f585018
Removed distutils package
terryluan12 Dec 30, 2025
da4a841
Updated doctest package
terryluan12 Dec 30, 2025
d7405c9
* Updated datetimetester.py
terryluan12 Dec 30, 2025
e82917e
Updated enum and test_enum.py
terryluan12 Dec 30, 2025
6e59ca5
Updated filecmp + test_filecmp
terryluan12 Dec 30, 2025
aae2dcf
Updated fractions + test_fractions
terryluan12 Dec 30, 2025
fd34286
Updated ftplib + test_ftplib
terryluan12 Dec 30, 2025
77db70c
Updated hmac + test_hmac
terryluan12 Dec 30, 2025
7c13c61
* Updated mailbox + added test_mailbox.py
terryluan12 Dec 30, 2025
3752174
Updated nturl2path.py
terryluan12 Dec 30, 2025
cd5865e
Added pathlib + test_pathlib packages
terryluan12 Dec 30, 2025
0e3365a
Updated pkgutil.py & test_pkgutil.py
terryluan12 Dec 30, 2025
d313adc
Updated platform.py + test_platform.py
terryluan12 Dec 30, 2025
6da715b
Updated plistlib + test_plistlib
terryluan12 Dec 30, 2025
2ee4e37
Merge branch 'add_tests' into update_simple_packages_2
terryluan12 Dec 30, 2025
28493a7
Updated enum and plistlib tests using the script
terryluan12 Dec 30, 2025
312f7d9
Updated pkgutil test with the script
terryluan12 Dec 30, 2025
39f990e
Ran/updated ftplib +test_hmac + pathlib tests with the script
terryluan12 Dec 30, 2025
1cd4ad6
Added comment to pathlib
terryluan12 Dec 30, 2025
88857d9
Clarified the comments at the top of test_pathlib
terryluan12 Dec 30, 2025
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
Prev Previous commit
Next Next commit
Updated ftplib + test_ftplib
  • Loading branch information
terryluan12 committed Dec 30, 2025
commit fd34286f447533fe25abf3486f9b0ff8da906da9
27 changes: 17 additions & 10 deletions Lib/ftplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,11 +900,17 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):

def test():
'''Test program.
Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
Usage: ftplib [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...

-d dir
-l list
-p password
Options:
-d increase debugging level
-r[file] set alternate ~/.netrc file

Commands:
-l[dir] list directory
-d[dir] change the current directory
-p toggle passive and active mode
file retrieve the file and write it to stdout
'''

if len(sys.argv) < 2:
Expand All @@ -930,15 +936,14 @@ def test():
netrcobj = netrc.netrc(rcfile)
except OSError:
if rcfile is not None:
sys.stderr.write("Could not open account file"
" -- using anonymous login.")
print("Could not open account file -- using anonymous login.",
file=sys.stderr)
else:
try:
userid, acct, passwd = netrcobj.authenticators(host)
except KeyError:
except (KeyError, TypeError):
# no account for host
sys.stderr.write(
"No account -- using anonymous login.")
print("No account -- using anonymous login.", file=sys.stderr)
ftp.login(userid, passwd, acct)
for file in sys.argv[2:]:
if file[:2] == '-l':
Expand All @@ -951,7 +956,9 @@ def test():
ftp.set_pasv(not ftp.passiveserver)
else:
ftp.retrbinary('RETR ' + file, \
sys.stdout.write, 1024)
sys.stdout.buffer.write, 1024)
sys.stdout.buffer.flush()
sys.stdout.flush()
ftp.quit()


Expand Down
83 changes: 53 additions & 30 deletions Lib/test/test_ftplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from unittest import TestCase, skipUnless
from test import support
from test.support import requires_subprocess
from test.support import threading_helper
from test.support import socket_helper
from test.support import warnings_helper
Expand All @@ -32,7 +33,7 @@
DEFAULT_ENCODING = 'utf-8'
# the dummy data returned by server over the data channel when
# RETR, LIST, NLST, MLSD commands are issued
RETR_DATA = 'abcde12345\r\n' * 1000 + 'non-ascii char \xAE\r\n'
RETR_DATA = 'abcde\xB9\xB2\xB3\xA4\xA6\r\n' * 1000
LIST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
NLST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
MLSD_DATA = ("type=cdir;perm=el;unique==keVO1+ZF4; test\r\n"
Expand Down Expand Up @@ -67,11 +68,11 @@ class DummyDTPHandler(asynchat.async_chat):
def __init__(self, conn, baseclass):
asynchat.async_chat.__init__(self, conn)
self.baseclass = baseclass
self.baseclass.last_received_data = ''
self.baseclass.last_received_data = bytearray()
self.encoding = baseclass.encoding

def handle_read(self):
new_data = self.recv(1024).decode(self.encoding, 'replace')
new_data = self.recv(1024)
self.baseclass.last_received_data += new_data

def handle_close(self):
Expand All @@ -80,7 +81,7 @@ def handle_close(self):
# (behaviour witnessed with test_data_connection)
if not self.dtp_conn_closed:
self.baseclass.push('226 transfer complete')
self.close()
self.shutdown()
self.dtp_conn_closed = True

def push(self, what):
Expand All @@ -94,6 +95,9 @@ def push(self, what):
def handle_error(self):
default_error_handler()

def shutdown(self):
self.close()


class DummyFTPHandler(asynchat.async_chat):

Expand All @@ -107,7 +111,7 @@ def __init__(self, conn, encoding=DEFAULT_ENCODING):
self.in_buffer = []
self.dtp = None
self.last_received_cmd = None
self.last_received_data = ''
self.last_received_data = bytearray()
self.next_response = ''
self.next_data = None
self.rest = None
Expand Down Expand Up @@ -226,7 +230,7 @@ def cmd_type(self, arg):

def cmd_quit(self, arg):
self.push('221 quit ok')
self.close()
self.shutdown()

def cmd_abor(self, arg):
self.push('226 abor ok')
Expand Down Expand Up @@ -313,7 +317,7 @@ def handle_accepted(self, conn, addr):
self.handler_instance = self.handler(conn, encoding=self.encoding)

def handle_connect(self):
self.close()
self.shutdown()
handle_read = handle_connect

def writable(self):
Expand All @@ -325,8 +329,8 @@ def handle_error(self):

if ssl is not None:

CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem")
CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem")
CERTFILE = os.path.join(os.path.dirname(__file__), "certdata", "keycert3.pem")
CAFILE = os.path.join(os.path.dirname(__file__), "certdata", "pycacert.pem")

class SSLConnection(asyncore.dispatcher):
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
Expand Down Expand Up @@ -425,12 +429,12 @@ def recv(self, buffer_size):
def handle_error(self):
default_error_handler()

def close(self):
def shutdown(self):
if (isinstance(self.socket, ssl.SSLSocket) and
self.socket._sslobj is not None):
self._do_ssl_shutdown()
else:
super(SSLConnection, self).close()
self.close()


class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
Expand Down Expand Up @@ -542,8 +546,8 @@ def test_set_pasv(self):
self.assertFalse(self.client.passiveserver)

def test_voidcmd(self):
self.client.voidcmd('echo 200')
self.client.voidcmd('echo 299')
self.assertEqual(self.client.voidcmd('echo 200'), '200')
self.assertEqual(self.client.voidcmd('echo 299'), '299')
self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')

Expand Down Expand Up @@ -589,21 +593,25 @@ def test_quit(self):
def test_abort(self):
self.client.abort()

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_retrbinary(self):
def callback(data):
received.append(data.decode(self.client.encoding))
received = []
self.client.retrbinary('retr', callback)
self.check_data(''.join(received), RETR_DATA)
self.client.retrbinary('retr', received.append)
self.check_data(b''.join(received),
RETR_DATA.encode(self.client.encoding))

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_retrbinary_rest(self):
def callback(data):
received.append(data.decode(self.client.encoding))
for rest in (0, 10, 20):
received = []
self.client.retrbinary('retr', callback, rest=rest)
self.check_data(''.join(received), RETR_DATA[rest:])
self.client.retrbinary('retr', received.append, rest=rest)
self.check_data(b''.join(received),
RETR_DATA[rest:].encode(self.client.encoding))

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_retrlines(self):
received = []
self.client.retrlines('retr', received.append)
Expand All @@ -613,13 +621,16 @@ def test_retrlines(self):
def test_storbinary(self):
f = io.BytesIO(RETR_DATA.encode(self.client.encoding))
self.client.storbinary('stor', f)
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
self.check_data(self.server.handler_instance.last_received_data,
RETR_DATA.encode(self.server.encoding))
# test new callback arg
flag = []
f.seek(0)
self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
self.assertTrue(flag)

@unittest.skip('TODO: RUSTPYTHON')
# ssl_error.SSLWantReadError: The operation did not complete (read)
def test_storbinary_rest(self):
data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding)
f = io.BytesIO(data)
Expand All @@ -628,11 +639,14 @@ def test_storbinary_rest(self):
self.client.storbinary('stor', f, rest=r)
self.assertEqual(self.server.handler_instance.rest, str(r))

@unittest.skip('TODO: RUSTPYTHON')
# ssl_error.SSLWantReadError: The operation did not complete (read)
def test_storlines(self):
data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding)
f = io.BytesIO(data)
self.client.storlines('stor', f)
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
self.check_data(self.server.handler_instance.last_received_data,
RETR_DATA.encode(self.server.encoding))
# test new callback arg
flag = []
f.seek(0)
Expand All @@ -644,15 +658,21 @@ def test_storlines(self):
with warnings_helper.check_warnings(('', BytesWarning), quiet=True):
self.assertRaises(TypeError, self.client.storlines, 'stor foo', f)

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_nlst(self):
self.client.nlst()
self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_dir(self):
l = []
self.client.dir(lambda x: l.append(x))
self.client.dir(l.append)
self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_mlsd(self):
list(self.client.mlsd())
list(self.client.mlsd(path='/'))
Expand Down Expand Up @@ -839,6 +859,8 @@ def test_storlines_too_long(self):
f = io.BytesIO(b'x' * self.client.maxline * 2)
self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f)

@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_encoding_param(self):
encodings = ['latin-1', 'utf-8']
for encoding in encodings:
Expand Down Expand Up @@ -890,12 +912,10 @@ def test_makepasv(self):

def test_transfer(self):
def retr():
def callback(data):
received.append(data.decode(self.client.encoding))
received = []
self.client.retrbinary('retr', callback)
self.assertEqual(len(''.join(received)), len(RETR_DATA))
self.assertEqual(''.join(received), RETR_DATA)
self.client.retrbinary('retr', received.append)
self.assertEqual(b''.join(received),
RETR_DATA.encode(self.client.encoding))
self.client.set_pasv(True)
retr()
self.client.set_pasv(False)
Expand All @@ -904,6 +924,7 @@ def callback(data):

@skipUnless(ssl, "SSL not available")
@unittest.skip("TODO: RUSTPYTHON; figure out why do_handshake() is throwing 'ssl session has been shut down'. SslSession object?")
@requires_subprocess()
class TestTLS_FTPClassMixin(TestFTPClass):
"""Repeat TestFTPClass tests starting the TLS layer for both control
and data connections first.
Expand All @@ -920,7 +941,7 @@ def setUp(self, encoding=DEFAULT_ENCODING):


@skipUnless(ssl, "SSL not available")
@unittest.skip("TODO: RUSTPYTHON; fix ssl")
@requires_subprocess()
class TestTLS_FTPClass(TestCase):
"""Specific TLS_FTP class tests."""

Expand Down Expand Up @@ -1004,6 +1025,8 @@ def test_context(self):
self.assertIs(sock.context, ctx)
self.assertIsInstance(sock, ssl.SSLSocket)

@unittest.skip('TODO: RUSTPYTHON')
# ssl_error.SSLWantReadError: The operation did not complete (read)
def test_ccc(self):
self.assertRaises(ValueError, self.client.ccc)
self.client.login(secure=True)
Expand Down