Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(2402)

Unified Diff: Lib/pdb.py

Issue 14913: tokenize the source to manage Pdb breakpoints
Patch Set: Created 1 year ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Lib/bdb.py ('k') | Lib/test/test_pdb.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -258,7 +258,7 @@
def user_line(self, frame):
"""This function is called when we stop or break at this line."""
if self._wait_for_mainpyfile:
- if (self.mainpyfile != self.canonic(frame.f_code.co_filename)
+ if (self.mainpyfile != bdb.canonic(frame.f_code.co_filename)
or frame.f_lineno <= 0):
return
self._wait_for_mainpyfile = False
@@ -266,27 +266,38 @@
self.interaction(frame, None)
def bp_commands(self, frame):
- """Call every command that was set for the current active breakpoint
- (if there is one).
+ """Call every command that was set for the current active breakpoints.
Returns True if the normal interaction function must be called,
False otherwise."""
- # self.currentbp is set in bdb in Bdb.break_here if a breakpoint was hit
- if getattr(self, "currentbp", False) and \
- self.currentbp in self.commands:
- currentbp = self.currentbp
- self.currentbp = 0
- lastcmd_back = self.lastcmd
- self.setup(frame, None)
- for line in self.commands[currentbp]:
- self.onecmd(line)
- self.lastcmd = lastcmd_back
- if not self.commands_silent[currentbp]:
- self.print_stack_entry(self.stack[self.curindex])
- if self.commands_doprompt[currentbp]:
- self._cmdloop()
- self.forget()
- return
+ # Handle multiple breakpoints on the same line (issue 14789)
+ # self.effective_bp_list is set in bdb in Bdb.break_here if breakpoints
+ # were hit
+ if hasattr(self, 'effective_bp_list') and self.effective_bp_list:
+ silent = True
+ doprompt = False
+ atleast_one_cmd = False
+ for bp in self.effective_bp_list:
+ if bp in self.commands:
+ atleast_one_cmd = True
+ lastcmd_back = self.lastcmd
+ self.setup(frame, None)
+ for line in self.commands[bp]:
+ self.onecmd(line)
+ self.lastcmd = lastcmd_back
+ if not self.commands_silent[bp]:
+ silent = False
+ if self.commands_doprompt[bp]:
+ doprompt = True
+
+ self.effective_bp_list = []
+ if atleast_one_cmd:
+ if not silent:
+ self.print_stack_entry(self.stack[self.curindex])
+ if doprompt:
+ self._cmdloop()
+ self.forget()
+ return
return 1
def user_return(self, frame, return_value):
@@ -602,81 +613,69 @@
sys.path; the .py suffix may be omitted.
"""
if not arg:
- if self.breaks: # There's at least one
+ if self.has_breaks(): # There's at least one.
self.message("Num Type Disp Enb Where")
for bp in bdb.Breakpoint.bpbynumber:
if bp:
self.message(bp.bpformat())
return
- # parse arguments; comma has lowest precedence
- # and cannot occur in filename
- filename = None
- lineno = None
+ # Parse arguments, comma has lowest precedence and cannot occur in
+ # filename.
cond = None
comma = arg.find(',')
if comma > 0:
# parse stuff after comma: "condition"
cond = arg[comma+1:].lstrip()
arg = arg[:comma].rstrip()
+
# parse stuff before comma: [filename:]lineno | function
- colon = arg.rfind(':')
- funcname = None
- if colon >= 0:
- filename = arg[:colon].rstrip()
- f = self.lookupmodule(filename)
- if not f:
- self.error('%r not found from sys.path' % filename)
- return
- else:
- filename = f
- arg = arg[colon+1:].lstrip()
+ i = arg.rfind(':')
+ if i >= 0:
+ filename = arg[:i].strip()
+ if not filename:
+ filename = self.curframe.f_code.co_filename
try:
- lineno = int(arg)
+ lineno = int(arg[i+1:])
except ValueError:
- self.error('Bad lineno: %s' % arg)
+ self.error('Bad lineno: "{}".'.format(arg))
return
else:
- # no colon; can be lineno or function
try:
+ # A line number
lineno = int(arg)
+ filename = self.curframe.f_code.co_filename
except ValueError:
+ # A function or method name
try:
- func = eval(arg,
- self.curframe.f_globals,
- self.curframe_locals)
- except:
- func = arg
- try:
- if hasattr(func, '__func__'):
- func = func.__func__
- code = func.__code__
- #use co_name to identify the bkpt (function names
- #could be aliased, but co_name is invariant)
- funcname = code.co_name
- lineno = code.co_firstlineno
- filename = code.co_filename
- except:
- # last thing to try
- (ok, filename, ln) = self.lineinfo(arg)
- if not ok:
- self.error('The specified object %r is not a function '
- 'or was not found along sys.path.' % arg)
- return
- funcname = ok # ok contains a function name
- lineno = int(ln)
- if not filename:
- filename = self.defaultFile()
- # Check for reasonable breakpoint
- line = self.checkline(filename, lineno)
- if line:
- # now set the break point
- err = self.set_break(filename, line, temporary, cond, funcname)
- if err:
- self.error(err, file=self.stdout)
- else:
- bp = self.get_breaks(filename, line)[-1]
- self.message("Breakpoint %d at %s:%d" %
- (bp.number, bp.file, bp.line))
+ filename, lineno = bdb.funcname_breakpoint(arg.strip(),
+ frame=self.curframe)
+ except bdb.BdbError as e:
+ self.error(e.args[0])
+ return
+
+ filename = bdb.canonic(filename)
+ if filename.startswith('<') and filename.endswith('>'):
+ if filename == '<string>' and self.mainpyfile:
+ filename = self.mainpyfile
+ # else
+ # doctest installs a patch at linecache.getlines to allow <doctest
+ # name> to be linecached and readable.
+ else:
+ root, ext = os.path.splitext(filename)
+ if ext == '':
+ filename = filename + '.py'
+ if not os.path.exists(filename):
+ self.error('Bad filename: "{}".'.format(arg))
+ return
+
+ # Now set the break point
+ error = self.set_break(filename, lineno, temporary, cond)
+ if error:
+ self.error(error)
+ else:
+ bp = self.get_breaks(filename, lineno)[-1]
+ self.message('Breakpoint {:d} at {}:{:d}'.format(
+ bp.number, bp.file, bp.line))
# To be overridden in derived debuggers
def defaultFile(self):
@@ -700,60 +699,6 @@
complete_tbreak = _complete_location
- def lineinfo(self, identifier):
- failed = (None, None, None)
- # Input is identifier, may be in single quotes
- idstring = identifier.split("'")
- if len(idstring) == 1:
- # not in single quotes
- id = idstring[0].strip()
- elif len(idstring) == 3:
- # quoted
- id = idstring[1].strip()
- else:
- return failed
- if id == '': return failed
- parts = id.split('.')
- # Protection for derived debuggers
- if parts[0] == 'self':
- del parts[0]
- if len(parts) == 0:
- return failed
- # Best first guess at file to look at
- fname = self.defaultFile()
- if len(parts) == 1:
- item = parts[0]
- else:
- # More than one part.
- # First is module, second is method/class
- f = self.lookupmodule(parts[0])
- if f:
- fname = f
- item = parts[1]
- answer = find_function(item, fname)
- return answer or failed
-
- def checkline(self, filename, lineno):
- """Check whether specified line seems to be executable.
-
- Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
- line or EOF). Warning: testing is not comprehensive.
- """
- # this method should be callable before starting debugging, so default
- # to "no globals" if there is no current frame
- globs = self.curframe.f_globals if hasattr(self, 'curframe') else None
- line = linecache.getline(filename, lineno, globs)
- if not line:
- self.message('End of file')
- return 0
- line = line.strip()
- # Don't allow setting breakpoint at a blank line
- if (not line or (line[0] == '#') or
- (line[:3] == '"""') or line[:3] == "'''"):
- self.error('Blank or comment')
- return 0
- return lineno
-
def do_enable(self, arg):
"""enable bpnumber [bpnumber ...]
Enables the breakpoints given as a space separated list of
@@ -1213,7 +1158,7 @@
first = self.lineno + 1
if last is None:
last = first + 10
- filename = self.curframe.f_code.co_filename
+ filename = bdb.canonic(self.curframe.f_code.co_filename)
breaklist = self.get_file_breaks(filename)
try:
lines = linecache.getlines(filename, self.curframe.f_globals)
@@ -1481,30 +1426,6 @@
# other helper functions
- def lookupmodule(self, filename):
- """Helper function for break/clear parsing -- may be overridden.
-
- lookupmodule() translates (possibly incomplete) file or module name
- into an absolute file name.
- """
- if os.path.isabs(filename) and os.path.exists(filename):
- return filename
- f = os.path.join(sys.path[0], filename)
- if os.path.exists(f) and self.canonic(f) == self.mainpyfile:
- return f
- root, ext = os.path.splitext(filename)
- if ext == '':
- filename = filename + '.py'
- if os.path.isabs(filename):
- return filename
- for dirname in sys.path:
- while os.path.islink(dirname):
- dirname = os.readlink(dirname)
- fullname = os.path.join(dirname, filename)
- if os.path.exists(fullname):
- return fullname
- return None
-
def _runscript(self, filename):
# The script has to run in __main__ namespace (or imports from
# __main__ will break).
@@ -1524,7 +1445,7 @@
# avoid stopping before we reach the main script (see user_line and
# user_call for details).
self._wait_for_mainpyfile = True
- self.mainpyfile = self.canonic(filename)
+ self.mainpyfile = bdb.canonic(filename)
self._user_requested_quit = False
with open(filename, "rb") as fp:
statement = "exec(compile(%r, %r, 'exec'))" % \
« no previous file with comments | « Lib/bdb.py ('k') | Lib/test/test_pdb.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld cbc36f91f3f7