Python Tricks

     Introduction
          Running pdb under emacs
          Extensions to python-mode in emacs
          Emacs function to insert debugging print statement

Introduction

Every once in a while I am reduced to figuring something out for my self with very little help from the documentation. So I will jot them down here in hopes of sparing someone else a bit of confusion. If you can think of a better way to do any of these things, please let me know. If I agree I will add it here. If you have other suggestions, let me know and I will add it here.

Running pdb under emacs

This always intrigued me and so I spent the time to figure out a way for me to get it working. The steps are:

Extensions to python-mode in emacs

I am really addicted to debugging python programs from inside of emacs but I have wanted to see a couple of additional features.

py-mode-ext.el is a short bit of emacs lisp which I assert accomplishes the above. One just appends the contents of the file to his .emacs file.

Here are the contents of the file py-mode-ext.py:

; The stuff in this file augments python-mode.el
; Written by Don Rozenberg - rozen@mcn.org

; It provides a slightly different execution of the current buffer.
; It first reads from the minibuffer arguments to be sent to the program.
; It next saves all the modified buffer based on a query.
; It invokes python on the file corresponding to the current buffer.
; I do this because I want to be able to debug programs passing them the
; arguments that I will use after debugging. I often write programs with
; more than one module and finally, I don't want to froget to save
; some changes that I have made.
;
; In addition, there is a variation which invoked the python debugger on
; the file rather than the python interpreter thru gud.
;
; You can just append this file to your .emacs. It defines the keys:
;
;        (define-key py-mode-map "\C-c\C-c" 'py-run-prog)
;        (define-key py-mode-map "\C-c\C-g" 'py-call-pdb)))
;
; I did not figure out how to modify the Python menu.  If someone
; could help me with that I would be glad to include it.
;
; I had a time invoking the the python debugger in emacs. The trick which
; I finally used was to (a) move pdb.py to my home directory and make it
; executable, and (b) customize gud-pdb-command-name to be '~/pdb.py'.
;
; The buffer that I am editing is often different from the buffer that
; I am editing. So I have adopted the conventions of always have starting
; a program with a main function.  I load the module with main and  point
; register m to that function. Then when I want to run the program, I go to
; register m and give the key sequences to execute the program or to call
; pdb.  As a further help I have the file '~/.pdbrc' which contains the
; line 'break main' so that when I run the debugger it will put a
; at breakpoint function 'main'.

(defun py-execute-prog ()
  "Invoke python on the file being edited in the current buffer using
   arguments obtained from the minibuffer.  It will save all of the modified
   buffers before trying to execute the file."

  (interactive)
  (let* ((file (buffer-file-name (current-buffer)))
         (cmd (concat "python"))
         (args (cons
                file (py-chop-words
                      (read-from-minibuffer "Application args: "
                            py-execute-arg-history nil nil
                            '(py-execute-arg-history . 1))))))
    (save-some-buffers (not py-ask-about-save) nil)
    (if (get-buffer py-output-buffer)
        (kill-buffer py-output-buffer)) ; Get rid of buffer if it exists.
      (apply 'make-comint "Python Output" cmd nil args)
    (if (not (get-buffer py-output-buffer))
        (message "No output.")
      (setq py-exception-buffer (current-buffer))
      (let ((err-p (py-postprocess-output-buffer py-output-buffer)))
        (pop-to-buffer py-output-buffer)
        (if err-p
        (pop-to-buffer py-exception-buffer)))
      )))

(defun py-call-pdb ()
   "Invoke pdb on the current buffer, with arguments from the minibuffer.
    It will save all of the modified buffers before trying to execute the file.
    Note that this is a take off on py-execute-prog"
  (interactive)
  (require 'gud)
  (let* ((file (buffer-file-name (current-buffer)))
         (cmd (concat "python"))
         (args (concat
                gud-pdb-command-name " " file " "
                (read-from-minibuffer "Application args: "
                                      py-execute-arg-history nil nil
                                   '(py-execute-arg-history . 1)))))

    (save-some-buffers (not py-ask-about-save) nil) ; save changed files.
    (pdb args)))


;; Chop STRING into words separated by SPC or TAB and return a list of them.
;; Borrowed  from gud-chop-words.
(defun py-chop-words (string)
  (let ((i 0) (beg 0)
    (len (length string))
    (words nil))
    (while (< i len)
      (if (memq (aref string i) '(?\t ? ))
          (progn
            (setq words (cons (substring string beg i) words)
                  beg (1+ i))
            (while (and (< beg len) (memq (aref string beg) '(?\t ? )))
              (setq beg (1+ beg)))
            (setq i (1+ beg)))
        (setq i (1+ i))))
    (if (< beg len)
        (setq words (cons (substring string beg) words)))
    (nreverse words)))

(defvar py-execute-arg-history nil
"History of application arguments read from the minibuffer")

(add-hook 'python-mode-hook
      '(lambda ()
         (define-key py-mode-map "\C-c\C-c" 'py-execute-prog)
         (define-key py-mode-map "\C-c\C-g" 'py-call-pdb)))

Emacs function to insert debugging print statement

This emacs function will go to the end of the current line and insert a new line containing "print 'function: expr =', expr". The expr is picked up from the minibuffer following the prompt.

; This is a function which simplifies the insertion of debugging
; print statements in a python program.

(defun pyp (expr)
  "Insert a print statement as the next statement of the program.
   Include the name of the enclosing function or and accept from
   the minibuffer the expresion to be printed. The form of the
   statement to be entered and indented is:
      print 'function: expr =', expr"
  (interactive "sExpression to be printed: ")
  (save-excursion
	(py-beginning-of-def-or-class)
	(re-search-forward "[ ]*def[ ]+\\(\\w+\\)\\W")
	(setq pyp-funct (buffer-substring (match-beginning 1) (match-end 1))))
  (end-of-line)
  (newline)
  (insert "print '" pyp-funct ": " expr " =', " expr)
  (indent-for-tab-command))