diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 6a401a1af7eba3..c48f2319b43c5b 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -458,7 +458,8 @@ can be overridden by the local file. With a *function* argument, set a break at the first executable statement within that function. *function* can be any expression that evaluates to a function - in the current namespace. + in the current namespace, or in the form ``package.module.function`` if + ``package.module`` is importable. If a second argument is present, it is an expression which must evaluate to true before the breakpoint is honored. diff --git a/Lib/pdb.py b/Lib/pdb.py index db48c00d2f477d..114634a4469c13 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1688,10 +1688,10 @@ def lineinfo(self, identifier): else: # More than one part. # First is module, second is method/class - f = self.lookupmodule(parts[0]) + f = self.lookupmodule('.'.join(parts[:-1])) if f: fname = f - item = parts[1] + item = parts[-1] else: return failed answer = find_function(item, self.canonic(fname)) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 51a3e2212a13cf..fbd9535c94d5d1 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -514,6 +514,36 @@ def test_pdb_breakpoint_with_filename(): (Pdb) continue """ +def test_pdb_breakpoint_in_package(): + """Breakpoints for package.module.function should work even if the module is not yet imported. + + >>> def test_function(): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... import multiprocessing.util # Now import the module + ... multiprocessing.util.is_abstract_socket_namespace(None) # We should break here + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + ... 'multiprocessing.util.is_abstract_socket_namespace', # Make sure the module is not imported yet + ... 'break multiprocessing.util.is_abstract_socket_namespace', # We can still set a bp on it + ... 'continue', # will stop at the function call + ... 'clear 1', + ... 'continue', + ... ]): + ... test_function() + > (2)test_function() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) multiprocessing.util.is_abstract_socket_namespace + *** NameError: name 'multiprocessing' is not defined. Did you forget to import 'multiprocessing'? + (Pdb) break multiprocessing.util.is_abstract_socket_namespace + Breakpoint 1 at .../multiprocessing/util.py:... + (Pdb) continue + > .../multiprocessing/util.py(...)is_abstract_socket_namespace() + -> if not address: + (Pdb) clear 1 + Deleted breakpoint 1 at .../multiprocessing/util.py:... + (Pdb) continue + """ + def test_pdb_breakpoint_on_disabled_line(): """New breakpoint on once disabled line should work diff --git a/Misc/NEWS.d/next/Library/2026-10-21-15-10-00.gh-issue-160125.pdbpkg.rst b/Misc/NEWS.d/next/Library/2026-10-21-15-10-00.gh-issue-160125.pdbpkg.rst new file mode 100644 index 00000000000000..822d0dd3d79e91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-21-15-10-00.gh-issue-160125.pdbpkg.rst @@ -0,0 +1,3 @@ +Support setting breakpoints with the :mod:`pdb` ``break`` and ``tbreak`` +commands using ``package.module.function`` names when the module has not been +imported in the current namespace.