From: Nick Roberts
Typing 'clear 1' gives Deleted breakpoint 1' and typing 'clear fib.py:4' gives no message and can't be detected. If, in both cases, the the message is modified to somthing like
Deleted breakpoint 1 at /home/nick/python/fib.py:4
then I would have enough information.
The attached modifications do it for me. A couple of issues: - I completely replaced the Bdb.get_break method. It was returning a boolean indicating if the breakpoint existed or not, which appeared to be an error (get_breaks returned the list of breakpoints). The method was never used in bdb.py or pdb.py, so I replaced it with what I thought it should be. :-) That is, it now returns the breakpoint instance requested. If there is a reason, I can easily make this a new method (get_breakpoint/get_breakpointinstance) rather than replacing get_break. - I've duplicated logic from Bdb.clear_bpbynumber into a new method get_bpbynumber. The logic differs only in the return error messages or None. This seemed the simplest way to preserve the return value semantics convention of the Bdb class. - I'm also calling 'get', then 'clear' which does the work of 'get' twice. I did this to preserve the return value conventions of Bdb and make the breakpoint info available to the UI. Shouldn't be a performance issue, right? ;-) Suggestions before I submit these as a patch to SF? Life-doesn't-get-any-better-than-contributing-to-emacs-in-Python-ly y'rs, kb Sorry for the non-CVS diffs - I don't have a copy of the 2.3 repository right now: *** bdb.py.1.42 Fri Sep 19 08:08:17 2003 --- bdb.py Fri Sep 19 08:00:52 2003 *************** *** 266,274 **** self.breaks = {} def get_break(self, filename, lineno): ! filename = self.canonic(filename) ! return filename in self.breaks and \ ! lineno in self.breaks[filename] def get_breaks(self, filename, lineno): filename = self.canonic(filename) --- 266,275 ---- self.breaks = {} def get_break(self, filename, lineno): ! bplist = self.get_breaks( filename, lineno ) ! if len( bplist ): ! return bplist[-1] ! return None def get_breaks(self, filename, lineno): filename = self.canonic(filename) *************** *** 276,281 **** --- 277,294 ---- lineno in self.breaks[filename] and \ Breakpoint.bplist[filename, lineno] or [] + def get_bpbynumber(self, arg): + # duplicates logic from clear_bpbynumber + try: + number = int(arg) + except: + return None + try: + bp = Breakpoint.bpbynumber[number] + except IndexError: + return None + return bp + def get_file_breaks(self, filename): filename = self.canonic(filename) if filename in self.breaks: *** pdb.py.1.66 Fri Sep 19 08:06:01 2003 --- pdb.py Fri Sep 19 08:07:23 2003 *************** *** 467,482 **** except: err = "Invalid line number (%s)" % arg else: err = self.clear_break(filename, lineno) if err: print '***', err return numberlist = arg.split() for i in numberlist: err = self.clear_bpbynumber(i) if err: print '***', err else: ! print 'Deleted breakpoint %s ' % (i,) do_cl = do_clear # 'c' is already an abbreviation for 'continue' def do_where(self, arg): --- 467,486 ---- except: err = "Invalid line number (%s)" % arg else: + bp = self.get_break(filename, lineno) err = self.clear_break(filename, lineno) if err: print '***', err + else: + print 'Deleted breakpoint %s at %s:%s' % (i, bp.file, bp.line) return numberlist = arg.split() for i in numberlist: + bp = self.get_bpbynumber(i) err = self.clear_bpbynumber(i) if err: print '***', err else: ! print 'Deleted breakpoint %s at %s:%s' % (i, bp.file, bp.line) do_cl = do_clear # 'c' is already an abbreviation for 'continue' def do_where(self, arg):
Kevin J. Butler writes:
A couple of issues:
...
I know very little about python (!). I'm trying to extend what I have done in Emacs to other languages. What's the relationship between bdb.py and pdb.py? What message would your patch generate when a breakpoint is set/cleared? Nick
Nick Roberts wrote:
Kevin J. Butler writes:
A couple of issues:
...
I know very little about python (!). I'm trying to extend what I have done in Emacs to other languages. What's the relationship between bdb.py and pdb.py? What message would your patch generate when a breakpoint is set/cleared?
Sorry, bdb.py provides back-end features used by UI code in pdb.py - part of the fix went into each. After my patch: (Pdb) cl 3 Deleted breakpoint 3 at z:\work\wm\wm.py:136 (Pdb) cl wm.py:17 Deleted breakpoint 5 at z:\work\wm\wm.py:17 kb
Kevin J. Butler writes:
...
After my patch: (Pdb) cl 3 Deleted breakpoint 3 at z:\work\wm\wm.py:136 (Pdb) cl wm.py:17 Deleted breakpoint 5 at z:\work\wm\wm.py:17
I've applied your patch manually to my files bdb.py and pdb.py (I have 2.2) and it appears to work. If you load the file below in Emacs (M-x eval-buffer or M-x load-file), then type M-x pdb, the debugger (with your patch) should work with breakpoint icons in the display margin. Emacs must be a version from the CVS repository to work straight away. This will also give you buttons for debugging if you turn the toolbar on (M-x tool-bar-mode). I've tested it on GNU/Linux but it should work on Windows too if I've defined the regexp correctly. I've just noticed that you can enable/disable breakpoints in python. In my mode for gdb, the breakpoints are greyed out when the breakpoints become disabled. I could do this for python too if these commands were changed to emit a message. If your patches are accepted (by SF?) then I will integrate my changes into gud.el in the Emacs CVS repository. Nick ----------------------------------------------------- (require 'gud) (defun gud-sentinel (proc msg) (cond ((null (buffer-name (process-buffer proc))) ;; buffer killed ;; Stop displaying an arrow in a source file. (setq overlay-arrow-position nil) (set-process-buffer proc nil) (if (memq gud-minor-mode-type '(gdba pdb)) (gdb-reset) (gud-reset))) ((memq (process-status proc) '(signal exit)) ;; Stop displaying an arrow in a source file. (setq overlay-arrow-position nil) (with-current-buffer gud-comint-buffer (if (memq gud-minor-mode '(gdba pdb)) (gdb-reset) (gud-reset))) (let* ((obuf (current-buffer))) ;; save-excursion isn't the right thing if ;; process-buffer is current-buffer (unwind-protect (progn ;; Write something in *compilation* and hack its mode line, (set-buffer (process-buffer proc)) ;; Fix the mode line. (setq mode-line-process (concat ":" (symbol-name (process-status proc)))) (force-mode-line-update) (if (eobp) (insert ?\n mode-name " " msg) (save-excursion (goto-char (point-max)) (insert ?\n mode-name " " msg))) ;; If buffer and mode line will show that the process ;; is dead, we can delete it now. Otherwise it ;; will stay around until M-x list-processes. (delete-process proc)) ;; Restore old buffer, but don't restore old point ;; if obuf is the gud buffer. (set-buffer obuf)))))) (defun gdb-reset () "Exit a debugging session cleanly by killing the gdb buffers and resetting the source buffers." (dolist (buffer (buffer-list)) (if (not (eq buffer gud-comint-buffer)) (with-current-buffer buffer (if (memq gud-minor-mode '(gdba pdb)) (if (string-match "^\*.+*$" (buffer-name)) (kill-buffer nil) (if (display-images-p) (remove-images (point-min) (point-max)) (gdb-remove-strings (point-min) (point-max))) (setq left-margin-width 0) (setq gud-minor-mode nil) (kill-local-variable 'tool-bar-map) (setq gud-running nil) (if (get-buffer-window (current-buffer)) (set-window-margins (get-buffer-window (current-buffer)) left-margin-width right-margin-width)))))))) (defun gdb-put-string (putstring pos) "Put string PUTSTRING in front of POS in the current buffer. PUTSTRING is displayed by putting an overlay into the current buffer with a `before-string' STRING that has a `display' property whose value is PUTSTRING." (let ((gdb-string "x") (buffer (current-buffer))) (let ((overlay (make-overlay pos pos buffer)) (prop (list (list 'margin 'left-margin) putstring))) (put-text-property 0 (length gdb-string) 'display prop gdb-string) (overlay-put overlay 'put-break t) (overlay-put overlay 'before-string gdb-string)))) (defun gdb-remove-strings (start end &optional buffer) "Remove strings between START and END in BUFFER. Remove only strings that were put in BUFFER with calls to `put-string'. BUFFER nil or omitted means use the current buffer." (unless buffer (setq buffer (current-buffer))) (let ((overlays (overlays-in start end))) (while overlays (let ((overlay (car overlays))) (when (overlay-get overlay 'put-break) (delete-overlay overlay))) (setq overlays (cdr overlays))))) (defconst breakpoint-xpm-data "/* XPM */ static char *magick[] = { /* columns rows colors chars-per-pixel */ \"12 12 2 1\", \" c red\", \"+ c None\", /* pixels */ \"++++++++++++\", \"+++ +++\", \"++ ++\", \"+ +\", \"+ +\", \"+ +\", \"+ +\", \"+ +\", \"+ +\", \"++ ++\", \"+++ +++\", \"++++++++++++\" };" "XPM data used for breakpoint icon.") (defconst breakpoint-enabled-pbm-data "P1 12 12\", 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" "PBM data used for enabled breakpoint icon.") (defvar breakpoint-enabled-icon (find-image `((:type xpm :data ,breakpoint-xpm-data) (:type pbm :data ,breakpoint-enabled-pbm-data))) "Icon for enabled breakpoint in display margin") ;; ====================================================================== ;; pdb (Python debugger) functions ;; History of argument lists passed to pdb. (defvar gud-pdb-history nil) ;; Last group is for return value, e.g. "> test.py(2)foo()->None" ;; Either file or function name may be omitted: "> <string>(0)?()" (defvar gud-pdb-marker-regexp "^> \\([-a-zA-Z0-9_/.:\\]*\\|<string>\\)(\\([0-9]+\\))\\([a-zA-Z0-9_]*\\|\\?\\)()\\(->[^\n]*\\)?\n") (defvar gud-pdb-marker-regexp-file-group 1) (defvar gud-pdb-marker-regexp-line-group 2) (defvar gud-pdb-marker-regexp-fnname-group 3) (defvar gud-pdb-marker-regexp-start "^> ") (defvar gud-pdb-marker-regexp-breakpoint "reakpoint [0-9]+ at \\(\\([a-zA-Z]:\\)?[^:\n]*\\):\\([0-9]*\\)\n") ;; There's no guarantee that Emacs will hand the filter the entire ;; marker at once; it could be broken up across several strings. We ;; might even receive a big chunk with several markers in it. If we ;; receive a chunk of text which looks like it might contain the ;; beginning of a marker, we save it here between calls to the ;; filter. (defun gud-pdb-marker-filter (string) (setq gud-marker-acc (concat gud-marker-acc string)) (let ((output "")) ;; Process all the complete markers in this chunk. (while (string-match gud-pdb-marker-regexp gud-marker-acc) (setq ;; Extract the frame position from the marker. gud-last-frame (let ((file (match-string gud-pdb-marker-regexp-file-group gud-marker-acc)) (line (string-to-int (match-string gud-pdb-marker-regexp-line-group gud-marker-acc)))) (if (string-equal file "<string>") gud-last-frame (cons file line))) ;; Output everything instead of the below output (concat output (substring gud-marker-acc 0 (match-end 0))) ;; ;; Append any text before the marker to the output we're going ;; ;; to return - we don't include the marker in this text. ;; output (concat output ;; (substring gud-marker-acc 0 (match-beginning 0))) ;; Set the accumulator to the remaining text. gud-marker-acc (substring gud-marker-acc (match-end 0)))) (if (string-match (concat "B" gud-pdb-marker-regexp-breakpoint) gud-marker-acc) (let ((file (match-string 1 gud-marker-acc)) (line (match-string 3 gud-marker-acc))) (gud-pdb-insert-breakpoint file line))) (if (string-match (concat "Deleted b" gud-pdb-marker-regexp-breakpoint) gud-marker-acc) (let ((file (match-string 1 gud-marker-acc)) (line (match-string 3 gud-marker-acc))) (gud-pdb-remove-breakpoint file line))) ;; Does the remaining text look like it might end with the ;; beginning of another marker? If it does, then keep it in ;; gud-marker-acc until we receive the rest of it. Since we ;; know the full marker regexp above failed, it's pretty simple to ;; test for marker starts. (if (string-match gud-pdb-marker-regexp-start gud-marker-acc) (progn ;; Everything before the potential marker start can be output. (setq output (concat output (substring gud-marker-acc 0 (match-beginning 0)))) ;; Everything after, we save, to combine with later input. (setq gud-marker-acc (substring gud-marker-acc (match-beginning 0)))) (setq output (concat output gud-marker-acc) gud-marker-acc "")) output)) (defun gud-pdb-insert-breakpoint (file line) (with-current-buffer (find-file-noselect file) (save-current-buffer (set (make-local-variable 'gud-minor-mode) 'pdb) (set (make-local-variable 'tool-bar-map) gud-tool-bar-map) (setq left-margin-width 2) (if (get-buffer-window (current-buffer)) (set-window-margins (get-buffer-window (current-buffer)) left-margin-width right-margin-width))) (save-excursion (goto-line (string-to-number line)) (let ((start (progn (beginning-of-line) (- (point) 1))) (end (progn (end-of-line) (+ (point) 1)))) (if (display-images-p) (progn (remove-images start end) (put-image breakpoint-enabled-icon (+ start 1) "breakpoint icon enabled" 'left-margin)) (gdb-remove-strings start end) (gdb-put-string "B" (+ start 1))))))) (defun gud-pdb-remove-breakpoint (file line) (with-current-buffer (find-file-noselect file) (save-excursion (goto-line (string-to-number line)) (let ((start (progn (beginning-of-line) (- (point) 1))) (end (progn (end-of-line) (+ (point) 1)))) (if (display-images-p) (remove-images start end) (gdb-remove-strings start end)))))) (defcustom gud-pdb-command-name "pdb" "File name for executing the Python debugger. This should be an executable on your path, or an absolute file name." :type 'string :group 'gud) (defun pdb (command-line) "Run pdb on program FILE in buffer `*gud-FILE*'. The directory containing FILE becomes the initial working directory and source-file directory for your debugger." (interactive (list (gud-query-cmdline 'pdb))) (gud-common-init command-line nil 'gud-pdb-marker-filter) (set (make-local-variable 'gud-minor-mode) 'pdb) (gud-def gud-break "break %l" "\C-b" "Set breakpoint at current line.") (gud-def gud-remove "clear %f:%l" "\C-d" "Remove breakpoint at current line") (gud-def gud-step "step" "\C-s" "Step one source line with display.") (gud-def gud-next "next" "\C-n" "Step one line (skip functions).") (gud-def gud-cont "continue" "\C-r" "Continue with display.") (gud-def gud-finish "return" "\C-f" "Finish executing current function.") (gud-def gud-up "up" "<" "Up one stack frame.") (gud-def gud-down "down" ">" "Down one stack frame.") (gud-def gud-print "p %e" "\C-p" "Evaluate Python expression at point.") ;; Is this right? (gud-def gud-statement "! %e" "\C-e" "Execute Python statement at point.") ;; (setq comint-prompt-regexp "^(.*pdb[+]?) *") (setq comint-prompt-regexp "^(Pdb) *") (setq paragraph-start comint-prompt-regexp) (run-hooks 'pdb-mode-hook))
Nick Roberts wrote:
I've just noticed that you can enable/disable breakpoints in python. In my mode for gdb, the breakpoints are greyed out when the breakpoints become disabled. I could do this for python too if these commands were changed to emit a message.
Two additional lines will add that - see below.
If your patches are accepted (by SF?) then I will integrate my changes into gud.el in the Emacs CVS repository.
OK, I'll go ahead and submit them... However, what is the fallback behavior when emacs with the new gud.el encounters a pdb.py that doesn't have the patch? That is, will users of existing versions of python lose any functionality? kb *** pdb.py.p1 Sat Sep 20 09:54:20 2003 --- pdb.py Sat Sep 20 09:58:22 2003 *************** *** 388,393 **** --- 388,394 ---- bp = bdb.Breakpoint.bpbynumber[i] if bp: bp.enable() + print 'Enabled breakpoint %s at %s:%s' % (i, bp.file, bp.line) def do_disable(self, arg): args = arg.split() *************** *** 405,410 **** --- 406,412 ---- bp = bdb.Breakpoint.bpbynumber[i] if bp: bp.disable() + print 'Disabled breakpoint %s at %s:%s' % (i, bp.file, bp.line) def do_condition(self, arg): # arg is breakpoint number and condition
However, what is the fallback behavior when emacs with the new gud.el encounters a pdb.py that doesn't have the patch? That is, will users of existing versions of python lose any functionality?
The messages from clear, enable and disable will be new, so the existing behaviour will hold for existing versions of python. However, the message from break (and tbreak) will not have changed, but the way gud.el acts on it will. So, break will place a breakpoint icon that clear can't remove. However, these changes will go on the main trunk of the CVS version of Emacs which is unlikely to be released this year. Emacs development is relatively slow and the last non-bugfix release was Sepember 2001 (21.1). How often are new versions of Python released? Providing the next release of Python is before that of Emacs, I dont think there's a problem. Nick
Nick Roberts wrote:
However, what is the fallback behavior when emacs with the new gud.el encounters a pdb.py that doesn't have the patch? That is, will users of existing versions of python lose any functionality?
The messages from clear, enable and disable will be new, so the existing behaviour will hold for existing versions of python. However, the message from break (and tbreak) will not have changed, but the way gud.el acts on it will. So, break will place a breakpoint icon that clear can't remove.
However, these changes will go on the main trunk of the CVS version of Emacs which is unlikely to be released this year. Emacs development is relatively slow and the last non-bugfix release was Sepember 2001 (21.1). How often are new versions of Python released? Providing the next release of Python is before that of Emacs, I dont think there's a problem.
The frequency of Python releases is actually being debated as of this moment. Some have suggested having a release once a year (which seems to be winning unanimous support). If we stick with the old way (I couldn't count on it) then who knows; probably year to a year and a half. As for this thread, is this the proper place for this? Wouldn't on the python-mode mailing list (http://mail.python.org/mailman/listinfo/python-mode) be a better place for Emacs-related stuff? Or is my Vim bias influencing me too much? -Brett
Nick Roberts wrote:
However, what is the fallback behavior when emacs with the new gud.el > encounters a pdb.py that doesn't have the patch? That is, will users of > existing versions of python lose any functionality?
The messages from clear, enable and disable will be new, so the existing behaviour will hold for existing versions of python. However, the message from break (and tbreak) will not have changed, but the way gud.el acts on it will. So, break will place a breakpoint icon that clear can't remove.
However, these changes will go on the main trunk of the CVS version of Emacs which is unlikely to be released this year. Emacs development is relatively slow and the last non-bugfix release was Sepember 2001 (21.1). How often are new versions of Python released? Providing the next release of Python is before that of Emacs, I dont think there's a problem.
Well, older versions of Python tend to remain in usage long after newer versions are available - for instance, 1.5.2 is still the standard in some places, and there is virtually no chance of patching 1.5.2 for something like this. It sounds like the emacs-side misbehavior will be cosmetic only, so probably not too bad. Brett C. wrote:
As for this thread, is this the proper place for this? Wouldn't on the python-mode mailing list (http://mail.python.org/mailman/listinfo/python-mode) be a better place for Emacs-related stuff? Or is my Vim bias influencing me too much?
I think the Vim bias has is influencing you too much. It has been known to do that. ;-) (Speaking as a recovering/apostate vi user). I consider this more pdb-related than emacs-specific - I'd expect that integration with, say, Eclipse would benefit from this enhancement as well, so I've been hoping to get some feedback from the list... BTW, I've submitted the patch, it is 809887: http://sourceforge.net/tracker/index.php?func=detail&aid=809887&group_id=5470&atid=305470 kb
Kevin J. Butler writes:
...
I consider this more pdb-related than emacs-specific - I'd expect that integration with, say, Eclipse would benefit from this enhancement as well, so I've been hoping to get some feedback from the list...
BTW, I've submitted the patch, it is 809887: http://sourceforge.net/tracker/index.php?func=detail&aid=809887&group_id=5470&atid=305470
kb
The syntax of responses from pdb commands suggests to me that there is a fair amount of GDB in the underlying code. The GDB developers have written an interface called GDB/MI (MI = Machine Interface) for integration with an IDE. Since the Eclipse project is using this interface to integrate with GDB there may be some overlap for doing the same thing with with pdb. Nick
Nick Roberts wrote:
Kevin J. Butler writes:
...
I consider this more pdb-related than emacs-specific - I'd expect that integration with, say, Eclipse would benefit from this enhancement as well, so I've been hoping to get some feedback from the list...
BTW, I've submitted the patch, it is 809887: http://sourceforge.net/tracker/index.php?func=detail&aid=809887&group_id=5470&atid=305470
kb
The syntax of responses from pdb commands suggests to me that there is a fair amount of GDB in the underlying code.
Nope - similarity of interface only. pdb is pure python (and thus works in the Java implementation 'jython' as well), and I haven't heard that GDB has been incorporating python (yet ;-) ). kb
participants (4)
-
Brett C.
-
Kevin J. Butler
-
Kevin J. Butler
-
Nick Roberts