@@ -3542,7 +3542,15 @@ def exit_with_permission_help_text():
35423542 sys .exit (1 )
35433543
35443544
3545- def main ():
3545+ def parse_args ():
3546+ # We want pdb to be as intuitive as possible to users, so we need to do some
3547+ # heuristic parsing to deal with ambiguity.
3548+ # For example:
3549+ # "python -m pdb -m foo -p 1" should pass "-p 1" to "foo".
3550+ # "python -m pdb foo.py -m bar" should pass "-m bar" to "foo.py".
3551+ # "python -m pdb -m foo -m bar" should pass "-m bar" to "foo".
3552+ # This require some customized parsing logic to find the actual debug target.
3553+
35463554 import argparse
35473555
35483556 parser = argparse .ArgumentParser (
@@ -3553,58 +3561,57 @@ def main():
35533561 color = True ,
35543562 )
35553563
3556- # We need to maunally get the script from args, because the first positional
3557- # arguments could be either the script we need to debug, or the argument
3558- # to the -m module
3564+ # Get all the commands out first. For backwards compatibility, we allow
3565+ # -c commands to be after the target.
35593566 parser .add_argument ('-c' , '--command' , action = 'append' , default = [], metavar = 'command' , dest = 'commands' ,
35603567 help = 'pdb commands to execute as if given in a .pdbrc file' )
3561- parser .add_argument ('-m' , metavar = 'module' , dest = 'module' )
3562- parser .add_argument ('-p' , '--pid' , type = int , help = "attach to the specified PID" , default = None )
35633568
3564- if len (sys .argv ) == 1 :
3569+ opts , args = parser .parse_known_args ()
3570+
3571+ if not args :
35653572 # If no arguments were given (python -m pdb), print the whole help message.
35663573 # Without this check, argparse would only complain about missing required arguments.
3574+ # We need to add the arguments definitions here to get a proper help message.
3575+ parser .add_argument ('-m' , metavar = 'module' , dest = 'module' )
3576+ parser .add_argument ('-p' , '--pid' , type = int , help = "attach to the specified PID" , default = None )
35673577 parser .print_help ()
35683578 sys .exit (2 )
3579+ elif args [0 ] == '-p' or args [0 ] == '--pid' :
3580+ # Attach to a pid
3581+ parser .add_argument ('-p' , '--pid' , type = int , help = "attach to the specified PID" , default = None )
3582+ opts , args = parser .parse_known_args ()
3583+ if args :
3584+ # For --pid, any extra arguments are invalid.
3585+ parser .error (f"unrecognized arguments: { ' ' .join (args )} " )
3586+ elif args [0 ] == '-m' :
3587+ # Debug a module, we only need the first -m module argument.
3588+ # The rest is passed to the module itself.
3589+ parser .add_argument ('-m' , metavar = 'module' , dest = 'module' )
3590+ opt_module = parser .parse_args (args [:2 ])
3591+ opts .module = opt_module .module
3592+ args = args [2 :]
3593+ elif args [0 ].startswith ('-' ):
3594+ # Invalid argument before the script name.
3595+ invalid_args = list (itertools .takewhile (lambda a : a .startswith ('-' ), args ))
3596+ parser .error (f"unrecognized arguments: { ' ' .join (invalid_args )} " )
35693597
3570- opts , args = parser .parse_known_args ()
3598+ # Otherwise it's debugging a script and we already parsed all -c commands.
3599+
3600+ return opts , args
35713601
3572- if opts .pid :
3573- # If attaching to a remote pid, unrecognized arguments are not allowed.
3574- # This will raise an error if there are extra unrecognized arguments.
3575- opts = parser .parse_args ()
3576- if opts .module :
3577- parser .error ("argument -m: not allowed with argument --pid" )
3602+ def main ():
3603+ opts , args = parse_args ()
3604+
3605+ if getattr (opts , 'pid' , None ) is not None :
35783606 try :
35793607 attach (opts .pid , opts .commands )
35803608 except PermissionError as e :
35813609 exit_with_permission_help_text ()
35823610 return
3583- elif opts .module :
3584- # If a module is being debugged, we consider the arguments after "-m module" to
3585- # be potential arguments to the module itself. We need to parse the arguments
3586- # before "-m" to check if there is any invalid argument.
3587- # e.g. "python -m pdb -m foo --spam" means passing "--spam" to "foo"
3588- # "python -m pdb --spam -m foo" means passing "--spam" to "pdb" and is invalid
3589- idx = sys .argv .index ('-m' )
3590- args_to_pdb = sys .argv [1 :idx ]
3591- # This will raise an error if there are invalid arguments
3592- parser .parse_args (args_to_pdb )
3593- else :
3594- # If a script is being debugged, then pdb expects the script name as the first argument.
3595- # Anything before the script is considered an argument to pdb itself, which would
3596- # be invalid because it's not parsed by argparse.
3597- invalid_args = list (itertools .takewhile (lambda a : a .startswith ('-' ), args ))
3598- if invalid_args :
3599- parser .error (f"unrecognized arguments: { ' ' .join (invalid_args )} " )
3600- sys .exit (2 )
3601-
3602- if opts .module :
3611+ elif getattr (opts , 'module' , None ) is not None :
36033612 file = opts .module
36043613 target = _ModuleTarget (file )
36053614 else :
3606- if not args :
3607- parser .error ("no module or script to run" )
36083615 file = args .pop (0 )
36093616 if file .endswith ('.pyz' ):
36103617 target = _ZipTarget (file )
0 commit comments