@@ -3548,7 +3548,15 @@ def exit_with_permission_help_text():
35483548 sys .exit (1 )
35493549
35503550
3551- def main ():
3551+ def parse_args ():
3552+ # We want pdb to be as intuitive as possible to users, so we need to do some
3553+ # heuristic parsing to deal with ambiguity.
3554+ # For example:
3555+ # "python -m pdb -m foo -p 1" should pass "-p 1" to "foo".
3556+ # "python -m pdb foo.py -m bar" should pass "-m bar" to "foo.py".
3557+ # "python -m pdb -m foo -m bar" should pass "-m bar" to "foo".
3558+ # This require some customized parsing logic to find the actual debug target.
3559+
35523560 import argparse
35533561
35543562 parser = argparse .ArgumentParser (
@@ -3559,28 +3567,48 @@ def main():
35593567 color = True ,
35603568 )
35613569
3562- # We need to maunally get the script from args, because the first positional
3563- # arguments could be either the script we need to debug, or the argument
3564- # to the -m module
3570+ # Get all the commands out first. For backwards compatibility, we allow
3571+ # -c commands to be after the target.
35653572 parser .add_argument ('-c' , '--command' , action = 'append' , default = [], metavar = 'command' , dest = 'commands' ,
35663573 help = 'pdb commands to execute as if given in a .pdbrc file' )
3567- parser .add_argument ('-m' , metavar = 'module' , dest = 'module' )
3568- parser .add_argument ('-p' , '--pid' , type = int , help = "attach to the specified PID" , default = None )
35693574
3570- if len (sys .argv ) == 1 :
3575+ opts , args = parser .parse_known_args ()
3576+
3577+ if not args :
35713578 # If no arguments were given (python -m pdb), print the whole help message.
35723579 # Without this check, argparse would only complain about missing required arguments.
3580+ # We need to add the arguments definitions here to get a proper help message.
3581+ parser .add_argument ('-m' , metavar = 'module' , dest = 'module' )
3582+ parser .add_argument ('-p' , '--pid' , type = int , help = "attach to the specified PID" , default = None )
35733583 parser .print_help ()
35743584 sys .exit (2 )
3585+ elif args [0 ] == '-p' or args [0 ] == '--pid' :
3586+ # Attach to a pid
3587+ parser .add_argument ('-p' , '--pid' , type = int , help = "attach to the specified PID" , default = None )
3588+ opts , args = parser .parse_known_args ()
3589+ if args :
3590+ # For --pid, any extra arguments are invalid.
3591+ parser .error (f"unrecognized arguments: { ' ' .join (args )} " )
3592+ elif args [0 ] == '-m' :
3593+ # Debug a module, we only need the first -m module argument.
3594+ # The rest is passed to the module itself.
3595+ parser .add_argument ('-m' , metavar = 'module' , dest = 'module' )
3596+ opt_module = parser .parse_args (args [:2 ])
3597+ opts .module = opt_module .module
3598+ args = args [2 :]
3599+ elif args [0 ].startswith ('-' ):
3600+ # Invalid argument before the script name.
3601+ invalid_args = list (itertools .takewhile (lambda a : a .startswith ('-' ), args ))
3602+ parser .error (f"unrecognized arguments: { ' ' .join (invalid_args )} " )
35753603
3576- opts , args = parser .parse_known_args ()
3604+ # Otherwise it's debugging a script and we already parsed all -c commands.
3605+
3606+ return opts , args
35773607
3578- if opts .pid :
3579- # If attaching to a remote pid, unrecognized arguments are not allowed.
3580- # This will raise an error if there are extra unrecognized arguments.
3581- opts = parser .parse_args ()
3582- if opts .module :
3583- parser .error ("argument -m: not allowed with argument --pid" )
3608+ def main ():
3609+ opts , args = parse_args ()
3610+
3611+ if getattr (opts , 'pid' , None ) is not None :
35843612 try :
35853613 attach (opts .pid , opts .commands )
35863614 except RuntimeError :
@@ -3592,30 +3620,10 @@ def main():
35923620 except PermissionError :
35933621 exit_with_permission_help_text ()
35943622 return
3595- elif opts .module :
3596- # If a module is being debugged, we consider the arguments after "-m module" to
3597- # be potential arguments to the module itself. We need to parse the arguments
3598- # before "-m" to check if there is any invalid argument.
3599- # e.g. "python -m pdb -m foo --spam" means passing "--spam" to "foo"
3600- # "python -m pdb --spam -m foo" means passing "--spam" to "pdb" and is invalid
3601- idx = sys .argv .index ('-m' )
3602- args_to_pdb = sys .argv [1 :idx ]
3603- # This will raise an error if there are invalid arguments
3604- parser .parse_args (args_to_pdb )
3605- else :
3606- # If a script is being debugged, then pdb expects the script name as the first argument.
3607- # Anything before the script is considered an argument to pdb itself, which would
3608- # be invalid because it's not parsed by argparse.
3609- invalid_args = list (itertools .takewhile (lambda a : a .startswith ('-' ), args ))
3610- if invalid_args :
3611- parser .error (f"unrecognized arguments: { ' ' .join (invalid_args )} " )
3612-
3613- if opts .module :
3623+ elif getattr (opts , 'module' , None ) is not None :
36143624 file = opts .module
36153625 target = _ModuleTarget (file )
36163626 else :
3617- if not args :
3618- parser .error ("no module or script to run" )
36193627 file = args .pop (0 )
36203628 if file .endswith ('.pyz' ):
36213629 target = _ZipTarget (file )
0 commit comments