Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -2435,30 +2435,52 @@ def main():
parser.add_argument('-c', '--command', action='append', default=[], metavar='command', dest='commands',
help='pdb commands to execute as if given in a .pdbrc file')
parser.add_argument('-m', metavar='module', dest='module')
parser.add_argument('args', nargs='*',
help="when -m is not specified, the first arg is the script to debug")

if len(sys.argv) == 1:
# If no arguments were given (python -m pdb), print the whole help message.
# Without this check, argparse would only complain about missing required arguments.
parser.print_help()
sys.exit(2)

opts = parser.parse_args()
opts, args = parser.parse_known_args()

if opts.module:
# If a module is being debugged, we consider the arguments after "-m module" to
# be potential arguments to the module itself. We need to parse the arguments
# before "-m" to check if there is any invalid argument.
# e.g. "python -m pdb -m foo --spam" means passing "--spam" to "foo"
# "python -m pdb --spam -m foo" means passing "--spam" to "pdb" and is invalid
idx = sys.argv.index('-m')
args_to_pdb = sys.argv[1:idx]
# This will automatically raise an error if there are invalid arguments
parser.parse_args(args_to_pdb)
else:
# If a script is being debugged, then pdb expects a script as the first argument.
# Anything before the script is considered an argument to pdb itself, which would
# be invalid because it's not parsed by argparse.
invalid_args = []
for arg in args:
if not arg.startswith('-'):
break
invalid_args.append(arg)

if invalid_args:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for arg in args:
if not arg.startswith('-'):
break
invalid_args.append(arg)
if invalid_args:
if any(itertools.takewhile(lambda a: a.startswith('-'), args)):

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a better expression. Just one question - is it okay to import itertools in pdb? We do already use plenty of internal libraries, but there were concerns about using pdb to debug stdlib when they are not working.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we need to full list of invalid arguments to report to keep the old behavior I think. So we need to turn this takewhiel to a list and check that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pdb imports traceback which imports itertools.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the stdlib is broken you probably need to debug it on a previous version of python.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I used takewhile for invalid arguments now, but we do need the full list so I converted it to a list as well.

parser.error(f"unrecognized arguments: {' '.join(invalid_args)}")
sys.exit(2)

if opts.module:
file = opts.module
target = _ModuleTarget(file)
else:
if not opts.args:
if not args:
parser.error("no module or script to run")
file = opts.args.pop(0)
file = args.pop(0)
if file.endswith('.pyz'):
target = _ZipTarget(file)
else:
target = _ScriptTarget(file)

sys.argv[:] = [file] + opts.args # Hide "pdb.py" and pdb options from argument list
sys.argv[:] = [file] + args # Hide "pdb.py" and pdb options from argument list

# Note on saving/restoring sys.argv: it's a good idea when sys.argv was
# modified by the script being debugged. It's a bad idea when it was
Expand Down
21 changes: 20 additions & 1 deletion Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3052,6 +3052,7 @@ def _run_pdb(self, pdb_args, commands,
def run_pdb_script(self, script, commands,
expected_returncode=0,
extra_env=None,
script_args=None,
pdbrc=None,
remove_home=False):
"""Run 'script' lines with pdb and the pdb 'commands'."""
Expand All @@ -3069,7 +3070,9 @@ def run_pdb_script(self, script, commands,
if remove_home:
homesave = os.environ.pop('HOME', None)
try:
stdout, stderr = self._run_pdb([filename], commands, expected_returncode, extra_env)
if script_args is None:
script_args = []
stdout, stderr = self._run_pdb([filename] + script_args, commands, expected_returncode, extra_env)
finally:
if homesave is not None:
os.environ['HOME'] = homesave
Expand Down Expand Up @@ -3508,6 +3511,22 @@ def test_run_module_with_args(self):
stdout, _ = self._run_pdb(["-m", "calendar", "1"], commands)
self.assertIn("December", stdout)

stdout, _ = self._run_pdb(["-m", "calendar", "--type", "text"], commands)
self.assertIn("December", stdout)

def test_run_script_with_args(self):
script = """
import sys
print(sys.argv[1:])
"""
commands = """
continue
quit
"""

stdout, stderr = self.run_pdb_script(script, commands, script_args=["--bar", "foo"])
self.assertIn("['--bar', 'foo']", stdout)

def test_breakpoint(self):
script = """
if __name__ == '__main__':
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug in :mod:`pdb` where arguments starting with ``-`` can't be passed to the debugged script.
Loading