@@ -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 )
0 commit comments