| --- 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'))" % \ |