https://github.com/python/cpython/commit/c29f95a98310fb0a8a7d432741d4753872a57d0f
commit: c29f95a98310fb0a8a7d432741d4753872a57d0f
branch: main
author: Rudransh Shekhar <[email protected]>
committer: orsenthil <[email protected]>
date: 2026-11-01T20:06:55Z
summary:

gh-131178: Add tests for `ftplib` command-line interface (#161080)

* gh-131178: Add tests for ftplib command-line interface

Add unit tests in Lib/test/test_ftplib.py covering the ftplib command-line 
interface: verifying usage message on empty invocation, subprocess invocation, 
host connection with debuglevel, directory and passive mode commands, file 
retrieval, and custom netrc file error handling.


Co-authored-by: Senthil Kumaran <[email protected]>

files:
M Lib/test/test_ftplib.py

diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
index 983a8b92cf6384..0467c3c782b61d 100644
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -1191,6 +1191,74 @@ def test__all__(self):
         support.check__all__(self, ftplib, not_exported=not_exported)
 
 
+class CommandLineTest(TestCase):
+    """Tests for the ftplib command-line interface."""
+
+    def test_main_no_arguments_prints_usage(self):
+        with mock.patch('sys.argv', ['ftplib']), \
+             support.captured_stdout() as stdout:
+            with self.assertRaises(SystemExit) as cm:
+                ftplib.test()
+            self.assertEqual(cm.exception.code, 0)
+        output = stdout.getvalue()
+        self.assertIn('Usage:', output)
+        self.assertIn('Options:', output)
+        self.assertIn('Commands:', output)
+
+    @requires_subprocess()
+    def test_subprocess_invocation_no_args(self):
+        from test.support.script_helper import assert_python_ok
+        rc, out, err = assert_python_ok('-m', 'ftplib')
+        self.assertEqual(rc, 0)
+        self.assertIn(b'Usage:', out)
+        self.assertIn(b'Options:', out)
+        self.assertIn(b'Commands:', out)
+        self.assertEqual(err, b'')
+
+    @mock.patch('ftplib.FTP')
+    @mock.patch('netrc.netrc', side_effect=OSError)
+    def test_cli_host_and_debugging(self, mock_netrc, mock_ftp_cls):
+        mock_ftp = mock_ftp_cls.return_value
+        with mock.patch('sys.argv', ['ftplib', '-d', '-d', 'example.com']):
+            ftplib.test()
+        mock_ftp_cls.assert_called_once_with('example.com')
+        mock_ftp.set_debuglevel.assert_called_once_with(2)
+        mock_ftp.login.assert_called_once_with('', '', '')
+        mock_ftp.quit.assert_called_once()
+
+    @mock.patch('ftplib.FTP')
+    @mock.patch('netrc.netrc', side_effect=OSError)
+    def test_cli_commands(self, mock_netrc, mock_ftp_cls):
+        mock_ftp = mock_ftp_cls.return_value
+        with mock.patch('sys.argv', ['ftplib', 'example.com', '-l/pub', 
'-d/incoming', '-p']):
+            ftplib.test()
+        mock_ftp_cls.assert_called_once_with('example.com')
+        mock_ftp.dir.assert_called_once_with('/pub')
+        mock_ftp.sendcmd.assert_called_once_with('CWD /incoming')
+        mock_ftp.set_pasv.assert_called_once()
+        mock_ftp.quit.assert_called_once()
+
+    @mock.patch('ftplib.FTP')
+    @mock.patch('netrc.netrc', side_effect=OSError)
+    def test_cli_retrieve_file(self, mock_netrc, mock_ftp_cls):
+        mock_ftp = mock_ftp_cls.return_value
+        with mock.patch('sys.argv', ['ftplib', 'example.com', 'testfile.txt']):
+            ftplib.test()
+        mock_ftp.retrbinary.assert_called_once()
+        args, kwargs = mock_ftp.retrbinary.call_args
+        self.assertEqual(args[0], 'RETR testfile.txt')
+        mock_ftp.quit.assert_called_once()
+
+    @mock.patch('ftplib.FTP')
+    @mock.patch('netrc.netrc', side_effect=OSError('file not found'))
+    def test_cli_custom_rcfile_error(self, mock_netrc, mock_ftp_cls):
+        with mock.patch('sys.argv', ['ftplib', '-rnonexistent.netrc', 
'example.com']), \
+             support.captured_stderr() as stderr:
+            ftplib.test()
+        self.assertIn("Could not open account file -- using anonymous login.",
+                      stderr.getvalue())
+
+
 def setUpModule():
     thread_info = threading_helper.threading_setup()
     unittest.addModuleCleanup(threading_helper.threading_cleanup, *thread_info)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to