Skip to content

Commit 37e533a

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 299d020 + 7453b8f commit 37e533a

File tree

7 files changed

+86
-9
lines changed

7 files changed

+86
-9
lines changed

.github/ISSUE_TEMPLATE/bug.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ body:
3434
label: "CPython versions tested on:"
3535
multiple: true
3636
options:
37-
- "3.8"
3837
- "3.9"
3938
- "3.10"
4039
- "3.11"
4140
- "3.12"
4241
- "3.13"
42+
- "3.14"
4343
- "CPython main branch"
4444
validations:
4545
required: true

.github/ISSUE_TEMPLATE/crash.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ body:
2727
label: "CPython versions tested on:"
2828
multiple: true
2929
options:
30-
- "3.8"
3130
- "3.9"
3231
- "3.10"
3332
- "3.11"
3433
- "3.12"
3534
- "3.13"
35+
- "3.14"
3636
- "CPython main branch"
3737
validations:
3838
required: true

Lib/bdb.py

+8
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,14 @@ def set_break(self, filename, lineno, temporary=False, cond=None,
461461
return 'Line %s:%d does not exist' % (filename, lineno)
462462
self._add_to_breaks(filename, lineno)
463463
bp = Breakpoint(filename, lineno, temporary, cond, funcname)
464+
# After we set a new breakpoint, we need to search through all frames
465+
# and set f_trace to trace_dispatch if there could be a breakpoint in
466+
# that frame.
467+
frame = self.enterframe
468+
while frame:
469+
if self.break_anywhere(frame):
470+
frame.f_trace = self.trace_dispatch
471+
frame = frame.f_back
464472
return None
465473

466474
def _load_breaks(self):

Lib/pdb.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import inspect
8383
import textwrap
8484
import tokenize
85+
import itertools
8586
import traceback
8687
import linecache
8788
import _colorize
@@ -2433,30 +2434,47 @@ def main():
24332434
parser.add_argument('-c', '--command', action='append', default=[], metavar='command', dest='commands',
24342435
help='pdb commands to execute as if given in a .pdbrc file')
24352436
parser.add_argument('-m', metavar='module', dest='module')
2436-
parser.add_argument('args', nargs='*',
2437-
help="when -m is not specified, the first arg is the script to debug")
24382437

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

2445-
opts = parser.parse_args()
2444+
opts, args = parser.parse_known_args()
2445+
2446+
if opts.module:
2447+
# If a module is being debugged, we consider the arguments after "-m module" to
2448+
# be potential arguments to the module itself. We need to parse the arguments
2449+
# before "-m" to check if there is any invalid argument.
2450+
# e.g. "python -m pdb -m foo --spam" means passing "--spam" to "foo"
2451+
# "python -m pdb --spam -m foo" means passing "--spam" to "pdb" and is invalid
2452+
idx = sys.argv.index('-m')
2453+
args_to_pdb = sys.argv[1:idx]
2454+
# This will raise an error if there are invalid arguments
2455+
parser.parse_args(args_to_pdb)
2456+
else:
2457+
# If a script is being debugged, then pdb expects the script name as the first argument.
2458+
# Anything before the script is considered an argument to pdb itself, which would
2459+
# be invalid because it's not parsed by argparse.
2460+
invalid_args = list(itertools.takewhile(lambda a: a.startswith('-'), args))
2461+
if invalid_args:
2462+
parser.error(f"unrecognized arguments: {' '.join(invalid_args)}")
2463+
sys.exit(2)
24462464

24472465
if opts.module:
24482466
file = opts.module
24492467
target = _ModuleTarget(file)
24502468
else:
2451-
if not opts.args:
2469+
if not args:
24522470
parser.error("no module or script to run")
2453-
file = opts.args.pop(0)
2471+
file = args.pop(0)
24542472
if file.endswith('.pyz'):
24552473
target = _ZipTarget(file)
24562474
else:
24572475
target = _ScriptTarget(file)
24582476

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

24612479
# Note on saving/restoring sys.argv: it's a good idea when sys.argv was
24622480
# modified by the script being debugged. It's a bad idea when it was

Lib/test/test_pdb.py

+50-1
Original file line numberDiff line numberDiff line change
@@ -3089,6 +3089,7 @@ def _run_pdb(self, pdb_args, commands,
30893089
def run_pdb_script(self, script, commands,
30903090
expected_returncode=0,
30913091
extra_env=None,
3092+
script_args=None,
30923093
pdbrc=None,
30933094
remove_home=False):
30943095
"""Run 'script' lines with pdb and the pdb 'commands'."""
@@ -3106,7 +3107,9 @@ def run_pdb_script(self, script, commands,
31063107
if remove_home:
31073108
homesave = os.environ.pop('HOME', None)
31083109
try:
3109-
stdout, stderr = self._run_pdb([filename], commands, expected_returncode, extra_env)
3110+
if script_args is None:
3111+
script_args = []
3112+
stdout, stderr = self._run_pdb([filename] + script_args, commands, expected_returncode, extra_env)
31103113
finally:
31113114
if homesave is not None:
31123115
os.environ['HOME'] = homesave
@@ -3393,6 +3396,36 @@ def test_issue26053(self):
33933396
self.assertRegex(res, "Restarting .* with arguments:\na b c")
33943397
self.assertRegex(res, "Restarting .* with arguments:\nd e f")
33953398

3399+
def test_issue58956(self):
3400+
# Set a breakpoint in a function that already exists on the call stack
3401+
# should enable the trace function for the frame.
3402+
script = """
3403+
import bar
3404+
def foo():
3405+
ret = bar.bar()
3406+
pass
3407+
foo()
3408+
"""
3409+
commands = """
3410+
b bar.bar
3411+
c
3412+
b main.py:5
3413+
c
3414+
p ret
3415+
quit
3416+
"""
3417+
bar = """
3418+
def bar():
3419+
return 42
3420+
"""
3421+
with open('bar.py', 'w') as f:
3422+
f.write(textwrap.dedent(bar))
3423+
self.addCleanup(os_helper.unlink, 'bar.py')
3424+
stdout, stderr = self.run_pdb_script(script, commands)
3425+
lines = stdout.splitlines()
3426+
self.assertIn('-> pass', lines)
3427+
self.assertIn('(Pdb) 42', lines)
3428+
33963429
def test_step_into_botframe(self):
33973430
# gh-125422
33983431
# pdb should not be able to step into the botframe (bdb.py)
@@ -3559,6 +3592,22 @@ def test_run_module_with_args(self):
35593592
stdout, _ = self._run_pdb(["-m", "calendar", "1"], commands)
35603593
self.assertIn("December", stdout)
35613594

3595+
stdout, _ = self._run_pdb(["-m", "calendar", "--type", "text"], commands)
3596+
self.assertIn("December", stdout)
3597+
3598+
def test_run_script_with_args(self):
3599+
script = """
3600+
import sys
3601+
print(sys.argv[1:])
3602+
"""
3603+
commands = """
3604+
continue
3605+
quit
3606+
"""
3607+
3608+
stdout, stderr = self.run_pdb_script(script, commands, script_args=["--bar", "foo"])
3609+
self.assertIn("['--bar', 'foo']", stdout)
3610+
35623611
def test_breakpoint(self):
35633612
script = """
35643613
if __name__ == '__main__':
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in :mod:`pdb` where sometimes the breakpoint won't trigger if it was set on a function which is already in the call stack.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a bug in :mod:`pdb` where arguments starting with ``-`` can't be passed to the debugged script.

0 commit comments

Comments
 (0)