Skip to content

Commit 58c9d25

Browse files
[3.14] pythongh-125115 : Refactor the pdb parsing issue so positional arguments can pass through (pythonGH-140933) (python#141635)
(cherry-picked from commit 5348c20)
1 parent c9eb5cb commit 58c9d25

File tree

3 files changed

+48
-37
lines changed

3 files changed

+48
-37
lines changed

Lib/pdb.py

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,7 +3543,15 @@ def exit_with_permission_help_text():
35433543
sys.exit(1)
35443544

35453545

3546-
def main():
3546+
def parse_args():
3547+
# We want pdb to be as intuitive as possible to users, so we need to do some
3548+
# heuristic parsing to deal with ambiguity.
3549+
# For example:
3550+
# "python -m pdb -m foo -p 1" should pass "-p 1" to "foo".
3551+
# "python -m pdb foo.py -m bar" should pass "-m bar" to "foo.py".
3552+
# "python -m pdb -m foo -m bar" should pass "-m bar" to "foo".
3553+
# This require some customized parsing logic to find the actual debug target.
3554+
35473555
import argparse
35483556

35493557
parser = argparse.ArgumentParser(
@@ -3554,58 +3562,57 @@ def main():
35543562
color=True,
35553563
)
35563564

3557-
# We need to maunally get the script from args, because the first positional
3558-
# arguments could be either the script we need to debug, or the argument
3559-
# to the -m module
3565+
# Get all the commands out first. For backwards compatibility, we allow
3566+
# -c commands to be after the target.
35603567
parser.add_argument('-c', '--command', action='append', default=[], metavar='command', dest='commands',
35613568
help='pdb commands to execute as if given in a .pdbrc file')
3562-
parser.add_argument('-m', metavar='module', dest='module')
3563-
parser.add_argument('-p', '--pid', type=int, help="attach to the specified PID", default=None)
35643569

3565-
if len(sys.argv) == 1:
3570+
opts, args = parser.parse_known_args()
3571+
3572+
if not args:
35663573
# If no arguments were given (python -m pdb), print the whole help message.
35673574
# Without this check, argparse would only complain about missing required arguments.
3575+
# We need to add the arguments definitions here to get a proper help message.
3576+
parser.add_argument('-m', metavar='module', dest='module')
3577+
parser.add_argument('-p', '--pid', type=int, help="attach to the specified PID", default=None)
35683578
parser.print_help()
35693579
sys.exit(2)
3580+
elif args[0] == '-p' or args[0] == '--pid':
3581+
# Attach to a pid
3582+
parser.add_argument('-p', '--pid', type=int, help="attach to the specified PID", default=None)
3583+
opts, args = parser.parse_known_args()
3584+
if args:
3585+
# For --pid, any extra arguments are invalid.
3586+
parser.error(f"unrecognized arguments: {' '.join(args)}")
3587+
elif args[0] == '-m':
3588+
# Debug a module, we only need the first -m module argument.
3589+
# The rest is passed to the module itself.
3590+
parser.add_argument('-m', metavar='module', dest='module')
3591+
opt_module = parser.parse_args(args[:2])
3592+
opts.module = opt_module.module
3593+
args = args[2:]
3594+
elif args[0].startswith('-'):
3595+
# Invalid argument before the script name.
3596+
invalid_args = list(itertools.takewhile(lambda a: a.startswith('-'), args))
3597+
parser.error(f"unrecognized arguments: {' '.join(invalid_args)}")
35703598

3571-
opts, args = parser.parse_known_args()
3599+
# Otherwise it's debugging a script and we already parsed all -c commands.
3600+
3601+
return opts, args
35723602

3573-
if opts.pid:
3574-
# If attaching to a remote pid, unrecognized arguments are not allowed.
3575-
# This will raise an error if there are extra unrecognized arguments.
3576-
opts = parser.parse_args()
3577-
if opts.module:
3578-
parser.error("argument -m: not allowed with argument --pid")
3603+
def main():
3604+
opts, args = parse_args()
3605+
3606+
if getattr(opts, 'pid', None) is not None:
35793607
try:
35803608
attach(opts.pid, opts.commands)
35813609
except PermissionError as e:
35823610
exit_with_permission_help_text()
35833611
return
3584-
elif opts.module:
3585-
# If a module is being debugged, we consider the arguments after "-m module" to
3586-
# be potential arguments to the module itself. We need to parse the arguments
3587-
# before "-m" to check if there is any invalid argument.
3588-
# e.g. "python -m pdb -m foo --spam" means passing "--spam" to "foo"
3589-
# "python -m pdb --spam -m foo" means passing "--spam" to "pdb" and is invalid
3590-
idx = sys.argv.index('-m')
3591-
args_to_pdb = sys.argv[1:idx]
3592-
# This will raise an error if there are invalid arguments
3593-
parser.parse_args(args_to_pdb)
3594-
else:
3595-
# If a script is being debugged, then pdb expects the script name as the first argument.
3596-
# Anything before the script is considered an argument to pdb itself, which would
3597-
# be invalid because it's not parsed by argparse.
3598-
invalid_args = list(itertools.takewhile(lambda a: a.startswith('-'), args))
3599-
if invalid_args:
3600-
parser.error(f"unrecognized arguments: {' '.join(invalid_args)}")
3601-
sys.exit(2)
3602-
3603-
if opts.module:
3612+
elif getattr(opts, 'module', None) is not None:
36043613
file = opts.module
36053614
target = _ModuleTarget(file)
36063615
else:
3607-
if not args:
3608-
parser.error("no module or script to run")
36093616
file = args.pop(0)
36103617
if file.endswith('.pyz'):
36113618
target = _ZipTarget(file)

Lib/test/test_pdb.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3974,7 +3974,10 @@ def test_run_module_with_args(self):
39743974
commands = """
39753975
continue
39763976
"""
3977-
self._run_pdb(["calendar", "-m"], commands, expected_returncode=2)
3977+
self._run_pdb(["calendar", "-m"], commands, expected_returncode=1)
3978+
3979+
_, stderr = self._run_pdb(["-m", "calendar", "-p", "1"], commands)
3980+
self.assertIn("unrecognized arguments: -p", stderr)
39783981

39793982
stdout, _ = self._run_pdb(["-m", "calendar", "1"], commands)
39803983
self.assertIn("December", stdout)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Refactor the :mod:`pdb` parsing issue so positional arguments can pass through intuitively.

0 commit comments

Comments
 (0)