Skip to content

Commit f3f2bbb

Browse files
committed
pythongh-93096: Port a bunch of stdlib cli to ArgumentParser
1 parent a95e60d commit f3f2bbb

11 files changed

+178
-258
lines changed

Lib/filecmp.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,14 @@ def _filter(flist, skip):
298298
# Demonstration and testing.
299299
#
300300
def demo():
301-
import sys
302-
import getopt
303-
options, args = getopt.getopt(sys.argv[1:], 'r')
304-
if len(args) != 2:
305-
raise getopt.GetoptError('need exactly two args', None)
306-
dd = dircmp(args[0], args[1])
307-
if ('-r', '') in options:
301+
from argparse import ArgumentParser
302+
parser = ArgumentParser(description='compare directories and files')
303+
parser.add_argument('-r', action='store_true', help='recursive comparison')
304+
parser.add_argument('dir_a', help='one side of the comparison')
305+
parser.add_argument('dir_b', help='another side of the comparison')
306+
arguments = parser.parse_args()
307+
dd = dircmp(arguments['dir_a'], arguments['dir_b'])
308+
if arguments['r']:
308309
dd.report_full_closure()
309310
else:
310311
dd.report()

Lib/fileinput.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -423,20 +423,21 @@ def openhook(filename, mode):
423423
return openhook
424424

425425

426-
def _test():
427-
import getopt
428-
inplace = False
429-
backup = False
430-
opts, args = getopt.getopt(sys.argv[1:], "ib:")
431-
for o, a in opts:
432-
if o == '-i': inplace = True
433-
if o == '-b': backup = a
434-
for line in input(args, inplace=inplace, backup=backup):
435-
if line[-1:] == '\n': line = line[:-1]
436-
if line[-1:] == '\r': line = line[:-1]
437-
print("%d: %s[%d]%s %s" % (lineno(), filename(), filelineno(),
438-
isfirstline() and "*" or "", line))
439-
print("%d: %s[%d]" % (lineno(), filename(), filelineno()))
426+
def _cli():
427+
from argparse import ArgumentParser
428+
parser = ArgumentParser(description='show glued content of files')
429+
parser.add_argument('-i', '--inplace', action='store_true',
430+
help='access original files, not copies')
431+
parser.add_argument('-b', '--backup', action='store_true',
432+
help='file extension for the copies')
433+
parser.add_argument('file', nargs='+', help='input path')
434+
arguments = parser.parse_args()
435+
436+
for line in input(arguments.file, arguments.inplace, arguments.backup):
437+
line = line.rstrip('\r\n')
438+
start_flag = '*' if isfirstline() else ''
439+
print(f'{lineno()}: {filename()}[{filelineno()}]{start_flag} {line}')
440+
print(f'{lineno()}: {filename()}[{filelineno()}]')
440441

441442
if __name__ == '__main__':
442-
_test()
443+
_cli()

Lib/idlelib/pyshell.py

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,34 +1460,14 @@ def fix_x11_paste(root):
14601460
root.bind_class(cls, '<<Paste>>'))
14611461

14621462

1463-
usage_msg = """\
1464-
1465-
USAGE: idle [-deins] [-t title] [file]*
1466-
idle [-dns] [-t title] (-c cmd | -r file) [arg]*
1467-
idle [-dns] [-t title] - [arg]*
1468-
1469-
-h print this help message and exit
1470-
-n run IDLE without a subprocess (DEPRECATED,
1471-
see Help/IDLE Help for details)
1472-
1473-
The following options will override the IDLE 'settings' configuration:
1474-
1475-
-e open an edit window
1476-
-i open a shell window
1477-
1478-
The following options imply -i and will open a shell:
1479-
1480-
-c cmd run the command in a shell, or
1481-
-r file run script from file
1482-
1483-
-d enable the debugger
1484-
-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
1485-
-t title set title of shell window
1463+
usage="""%(prog)s -h
1464+
%(prog)s [-deins] [-t title] [file ...]
1465+
%(prog)s [-dns] [-t title] (-c cmd | -r file) [arg ...]
1466+
%(prog)s [-dns] [-t title] - [arg ...]"""
14861467

1468+
epilog = """\
14871469
A default edit window will be bypassed when -c, -r, or - are used.
14881470
1489-
[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
1490-
14911471
Examples:
14921472
14931473
idle
@@ -1511,30 +1491,45 @@ def fix_x11_paste(root):
15111491
15121492
echo "import sys; print(sys.argv)" | idle - "foobar"
15131493
Open a shell window, run the script piped in, passing '' in sys.argv[0]
1514-
and "foobar" in sys.argv[1].
1515-
"""
1494+
and "foobar" in sys.argv[1]."""
15161495

15171496
def main():
1518-
import getopt
1497+
from argparse import ArgumentParser, RawTextHelpFormatter
15191498
from platform import system
15201499
from idlelib import testing # bool value
15211500
from idlelib import macosx
15221501

15231502
global flist, root, use_subprocess
15241503

15251504
capture_warnings(True)
1526-
use_subprocess = True
1527-
enable_shell = False
1528-
enable_edit = False
1529-
debug = False
1530-
cmd = None
1531-
script = None
1532-
startup = False
1533-
try:
1534-
opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
1535-
except getopt.error as msg:
1536-
print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr)
1537-
sys.exit(2)
1505+
parser = ArgumentParser(description='Python’s Integrated Development and '
1506+
'Learning Environment', usage=usage, epilog=epilog,
1507+
formatter_class=RawTextHelpFormatter)
1508+
parser.add_argument('-n', action='store_true', help='run IDLE without a '
1509+
'subprocess (DEPRECATED, see Help/IDLE Help for '
1510+
'details)')
1511+
parser.add_argument('file', nargs='*', help='file path to open')
1512+
parser.add_argument('arg', nargs='*', help='parameter passed to the '
1513+
'command (-c), script (-r), or REPL (-) in '
1514+
'sys.argv[1:]')
1515+
settings = parser.add_argument_group(description="the following options "
1516+
"have priority over the IDLE "
1517+
"'settings' configuration:")
1518+
settings.add_argument('-e', '--edit', action='store_true', help='open an edit window')
1519+
settings.add_argument('-i', '--interactive', action='store_true', help='open a shell window')
1520+
shell = parser.add_argument_group(description='the following options '
1521+
'imply -i:')
1522+
run = parser.add_mutually_exclusive_group()
1523+
run.add_argument('-c', '--command', metavar='cmd',
1524+
help='run the command in a shell, or')
1525+
run.add_argument('-r', '--run', metavar='file', help='run script from file')
1526+
shell.add_argument('-d', '--debug', action='store_true', help='enable the debugger')
1527+
shell.add_argument('-s', action='store_true', help='run $IDLESTARTUP or '
1528+
'$PYTHONSTARTUP before anything else')
1529+
shell.add_argument('-t', '--title', metavar='title', help='set title of shell window')
1530+
1531+
arguments = parser.parse_args()
1532+
15381533
for o, a in opts:
15391534
if o == '-c':
15401535
cmd = a

Lib/imaplib.py

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,32 +1544,29 @@ def Time2Internaldate(date_time):
15441544

15451545
if __name__ == '__main__':
15461546

1547-
# To test: invoke either as 'python imaplib.py [IMAP4_server_hostname]'
1548-
# or 'python imaplib.py -s "rsh IMAP4_server_hostname exec /etc/rimapd"'
1549-
# to test the IMAP4_stream class
1547+
epilog = ("to test, invoke either as 'python imaplib.py "
1548+
"[IMAP4_server_hostname]' or 'python imaplib.py -s \"rsh "
1549+
"IMAP4_server_hostname exec /etc/rimapd\"' to test the "
1550+
"IMAP4_stream class")
15501551

1551-
import getopt, getpass
1552+
from argparse import ArgumentParser
1553+
import getpass
15521554

15531555
try:
1554-
optlist, args = getopt.getopt(sys.argv[1:], 'd:s:')
1556+
parser = ArgumentParser(description='test IMAP4rev1 servers',
1557+
epilog=epilog)
1558+
parser.add_argument('host', type=int, help='server hostname')
1559+
parser.add_argument('-d', type=int, help='debug output level')
1560+
parser.add_argument('-s', help=('execute a command line and attach '
1561+
'IMAP4_stream to it'))
1562+
arguments = parser.parse_args()
1563+
15551564
except getopt.error as val:
15561565
optlist, args = (), ()
15571566

1558-
stream_command = None
1559-
for opt,val in optlist:
1560-
if opt == '-d':
1561-
Debug = int(val)
1562-
elif opt == '-s':
1563-
stream_command = val
1564-
if not args: args = (stream_command,)
1565-
1566-
if not args: args = ('',)
1567-
1568-
host = args[0]
1569-
15701567
USER = getpass.getuser()
1571-
PASSWD = getpass.getpass("IMAP password for %s on %s: " % (USER, host or "localhost"))
1572-
1568+
host = arguments['host'] or "localhost"
1569+
PASSWD = getpass.getpass(f"IMAP password for {USER} on {host}: ")
15731570
test_mesg = 'From: %(user)s@localhost%(lf)sSubject: IMAP4 test%(lf)s%(lf)sdata...%(lf)s' % {'user':USER, 'lf':'\n'}
15741571
test_seq1 = (
15751572
('login', (USER, PASSWD)),
@@ -1606,10 +1603,7 @@ def run(cmd, args):
16061603
return dat
16071604

16081605
try:
1609-
if stream_command:
1610-
M = IMAP4_stream(stream_command)
1611-
else:
1612-
M = IMAP4(host)
1606+
M = IMAP4_stream(arguments['s']) if arguments['s'] else IMAP4(host)
16131607
if M.state == 'AUTH':
16141608
test_seq1 = test_seq1[1:] # Login not needed
16151609
M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
@@ -1640,7 +1634,7 @@ def run(cmd, args):
16401634
except:
16411635
print('\nTests failed.')
16421636

1643-
if not Debug:
1637+
if not arguments['d']:
16441638
print('''
16451639
If you would like to see debugging output,
16461640
try: %s -d5

Lib/modulefinder.py

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -600,67 +600,40 @@ def replace_paths_in_code(self, co):
600600
return co.replace(co_consts=tuple(consts), co_filename=new_filename)
601601

602602

603-
def test():
604-
# Parse command line
605-
import getopt
606-
try:
607-
opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:")
608-
except getopt.error as msg:
609-
print(msg)
610-
return
611-
612-
# Process options
613-
debug = 1
614-
domods = 0
615-
addpath = []
616-
exclude = []
617-
for o, a in opts:
618-
if o == '-d':
619-
debug = debug + 1
620-
if o == '-m':
621-
domods = 1
622-
if o == '-p':
623-
addpath = addpath + a.split(os.pathsep)
624-
if o == '-q':
625-
debug = 0
626-
if o == '-x':
627-
exclude.append(a)
628-
629-
# Provide default arguments
630-
if not args:
631-
script = "hello.py"
632-
else:
633-
script = args[0]
603+
def _cli():
604+
from argparse import ArgumentParser
605+
parser = ArgumentParser(description='list modules imported by a script')
606+
parser.add_argument('-d', '--debug', action='store_true', help='')
607+
parser.add_argument('-q', '--quiet', action='store_true', help='')
608+
parser.add_argument('-m', '--domods', action='store_true', help='')
609+
parser.add_argument('-p', '--path', nargs='*', help='path to search for modules; if not specified, sys.path is used')
610+
parser.add_argument('-x', '--exclude', nargs='*', help='')
611+
parser.add_argument('script', default='hello.py', help='path to a script')
612+
arguments = parser.parse_args()
634613

635614
# Set the path based on sys.path and the script directory
636615
path = sys.path[:]
637616
path[0] = os.path.dirname(script)
638-
path = addpath + path
639-
if debug > 1:
617+
path = arguments['path'] + path
618+
if arguments['debug']:
640619
print("path:")
641620
for item in path:
642621
print(" ", repr(item))
643622

644623
# Create the module finder and turn its crank
645-
mf = ModuleFinder(path, debug, exclude)
646-
for arg in args[1:]:
647-
if arg == '-m':
648-
domods = 1
649-
continue
650-
if domods:
651-
if arg[-2:] == '.*':
652-
mf.import_hook(arg[:-2], None, ["*"])
653-
else:
654-
mf.import_hook(arg)
655-
else:
656-
mf.load_file(arg)
624+
mf = ModuleFinder(arguments['path'], arguments['debug'], arguments['exclude'])
625+
if arguments['domods']
626+
for path in arguments['domods']:
627+
mf.import_hook(path, None, ["*"] if path == '.*' else None)
628+
else:
629+
mf.load_file(arg)
657630
mf.run_script(script)
658631
mf.report()
659632
return mf # for -i debugging
660633

661634

662635
if __name__ == '__main__':
663636
try:
664-
mf = test()
637+
mf = _cli()
665638
except KeyboardInterrupt:
666639
print("\n[interrupted]")

Lib/pdb.py

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,44 +1757,30 @@ def help():
17571757
import pydoc
17581758
pydoc.pager(__doc__)
17591759

1760-
_usage = """\
1761-
usage: pdb.py [-c command] ... [-m module | pyfile] [arg] ...
1762-
1763-
Debug the Python program given by pyfile. Alternatively,
1764-
an executable module or package to debug can be specified using
1765-
the -m switch.
1766-
1760+
epilog = """\
17671761
Initial commands are read from .pdbrc files in your home directory
1768-
and in the current directory, if they exist. Commands supplied with
1769-
-c are executed after commands from .pdbrc files.
1762+
and in the current directory, if they exist.
17701763
17711764
To let the script run until an exception occurs, use "-c continue".
17721765
To let the script run up to a given line X in the debugged file, use
17731766
"-c 'until X'"."""
17741767

17751768

1776-
def main():
1777-
import getopt
1778-
1779-
opts, args = getopt.getopt(sys.argv[1:], 'mhc:', ['help', 'command='])
1780-
1781-
if not args:
1782-
print(_usage)
1783-
sys.exit(2)
1784-
1785-
if any(opt in ['-h', '--help'] for opt, optarg in opts):
1786-
print(_usage)
1787-
sys.exit()
1788-
1789-
commands = [optarg for opt, optarg in opts if opt in ['-c', '--command']]
1790-
1791-
module_indicated = any(opt in ['-m'] for opt, optarg in opts)
1792-
cls = ModuleTarget if module_indicated else ScriptTarget
1793-
target = cls(args[0])
1769+
def _cli():
1770+
from argparse import ArgumentParser
1771+
parser = ArgumentParser(description='debug Python scripts given by pyfile '
1772+
'or -m module name', epilog=epilog)
1773+
parser.add_argument('-c', '--command', help='extra commands run after .pdbrc content')
1774+
parser.add_argument('-m', '--module', action='store_true', help='path is an -m module name')
1775+
parser.add_argument('path', help='debugged program')
1776+
parser.add_argument('arg', nargs='*', help='content of sys.argv')
1777+
arguments = parser.parse_args()
1778+
cls = ModuleTarget if 'module' in arguments else ScriptTarget
1779+
target = cls(arguments['path'])
17941780

17951781
target.check()
17961782

1797-
sys.argv[:] = args # Hide "pdb.py" and pdb options from argument list
1783+
sys.argv[:] = arguments[arg] # Hide "pdb.py" and pdb options from argument list
17981784

17991785
# Note on saving/restoring sys.argv: it's a good idea when sys.argv was
18001786
# modified by the script being debugged. It's a bad idea when it was
@@ -1830,5 +1816,4 @@ def main():
18301816

18311817
# When invoked as main program, invoke the debugger on a script
18321818
if __name__ == '__main__':
1833-
import pdb
1834-
pdb.main()
1819+
_cli()

0 commit comments

Comments
 (0)