]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/python.el
(compilation-start): Resurrect the version for systems that don't support
[gnu-emacs] / lisp / progmodes / python.el
index 9057e51e4393a1749188ec25c2fe595d8ca1b032..5fbd617be1010d1c48a5ae704b5363c9e7c3bd67 100644 (file)
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -20,9 +20,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -296,9 +294,7 @@ Used for syntactic keywords.  N is the match number (1, 2 or 3)."
        ("Templates..."
         :help "Expand templates for compound statements"
         :filter (lambda (&rest junk)
-                  (mapcar (lambda (elt)
-                            (vector (car elt) (cdr elt) t))
-                          python-skeletons))) ; defined later
+                   (abbrev-table-menu python-mode-abbrev-table)))
        "-"
        ["Start interpreter" python-shell
         :help "Run `inferior' Python in separate buffer"]
@@ -411,7 +407,7 @@ comments and strings, or that point is within brackets/parens."
                    (error nil))))))))
 
 (defun python-comment-line-p ()
-  "Return non-nil iff current line has only a comment."
+  "Return non-nil if and only if current line has only a comment."
   (save-excursion
     (end-of-line)
     (when (eq 'comment (syntax-ppss-context (syntax-ppss)))
@@ -419,7 +415,7 @@ comments and strings, or that point is within brackets/parens."
       (looking-at (rx (or (syntax comment-start) line-end))))))
 
 (defun python-blank-line-p ()
-  "Return non-nil iff current line is blank."
+  "Return non-nil if and only if current line is blank."
   (save-excursion
     (beginning-of-line)
     (looking-at "\\s-*$")))
@@ -545,15 +541,29 @@ mode buffer is visited during an Emacs session.  After that, use
 
 (defcustom python-pdbtrack-do-tracking-p t
   "*Controls whether the pdbtrack feature is enabled or not.
+
 When non-nil, pdbtrack is enabled in all comint-based buffers,
-e.g. shell buffers and the *Python* buffer.  When using pdb to debug a
-Python program, pdbtrack notices the pdb prompt and displays the
-source file and line that the program is stopped at, much the same way
-as gud-mode does for debugging C programs with gdb."
+e.g. shell interaction buffers and the *Python* buffer.
+
+When using pdb to debug a Python program, pdbtrack notices the
+pdb prompt and presents the line in the source file where the
+program is stopped in a pop-up buffer.  It's similar to what
+gud-mode does for debugging C programs with gdb, but without
+having to restart the program."
   :type 'boolean
   :group 'python)
 (make-variable-buffer-local 'python-pdbtrack-do-tracking-p)
 
+(defcustom python-pdbtrack-minor-mode-string " PDB"
+  "*Minor-mode sign to be displayed when pdbtrack is active."
+  :type 'string
+  :group 'python)
+
+;; Add a designator to the minor mode strings
+(or (assq 'python-pdbtrack-is-tracking-p minor-mode-alist)
+    (push '(python-pdbtrack-is-tracking-p python-pdbtrack-minor-mode-string)
+         minor-mode-alist))
+
 ;; Bind python-file-queue before installing the kill-emacs-hook.
 (defvar python-file-queue nil
   "Queue of Python temp files awaiting execution.
@@ -562,10 +572,10 @@ Currently-active file is at the head of the list.")
 (defvar python-pdbtrack-is-tracking-p nil)
 
 (defconst python-pdbtrack-stack-entry-regexp
-  "> \\([^(]+\\)(\\([0-9]+\\))[?a-zA-Z0-9_]+()"
+  "^> \\(.*\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_]+\\)()"
   "Regular expression pdbtrack uses to find a stack trace entry.")
 
-(defconst python-pdbtrack-input-prompt "\n[(<]?pdb[>)]? "
+(defconst python-pdbtrack-input-prompt "\n[(<]*[Pp]db[>)]+ "
   "Regular expression pdbtrack uses to recognize a pdb prompt.")
 
 (defconst python-pdbtrack-track-range 10000
@@ -735,7 +745,7 @@ Set `python-indent' locally to the value guessed."
   '(("else" "if" "elif" "while" "for" "try" "except")
     ("elif" "if" "elif")
     ("except" "try" "except")
-    ("finally" "try"))
+    ("finally" "try" "except"))
   "Alist of keyword matches.
 The car of an element is a keyword introducing a statement which
 can close a block opened by a keyword in the cdr.")
@@ -965,9 +975,11 @@ Accounts for continuation lines, multi-line strings, and
 multi-line bracketed expressions."
   (beginning-of-line)
   (python-beginning-of-string)
-  (let ((point (point)))
+  (let (point)
     (while (and (python-continuation-line-p)
-               (> point (setq point (point))))
+               (if point
+                   (< (point) point)
+                 t))
       (beginning-of-line)
       (if (python-backslash-continuation-line-p)
          (progn
@@ -975,14 +987,15 @@ multi-line bracketed expressions."
            (while (python-backslash-continuation-line-p)
              (forward-line -1)))
        (python-beginning-of-string)
-       (python-skip-out))))
+       (python-skip-out))
+      (setq point (point))))
   (back-to-indentation))
 
 (defun python-skip-out (&optional forward syntax)
   "Skip out of any nested brackets.
 Skip forward if FORWARD is non-nil, else backward.
 If SYNTAX is non-nil it is the state returned by `syntax-ppss' at point.
-Return non-nil iff skipping was done."
+Return non-nil if and only if skipping was done."
   (let ((depth (syntax-ppss-depth (or syntax (syntax-ppss))))
        (forward (if forward -1 1)))
     (unless (zerop depth)
@@ -1534,7 +1547,9 @@ buffer for a list of commands.)"
   ;; invoked.  Would support multiple processes better.
   (when (or new (not (comint-check-proc python-buffer)))
     (with-current-buffer
-       (let* ((cmdlist (append (python-args-to-list cmd) '("-i")))
+       (let* ((cmdlist
+               (append (python-args-to-list cmd)
+                       '("-i" "-c" "import sys; sys.path.remove('')")))
               (path (getenv "PYTHONPATH"))
               (process-environment     ; to import emacs.py
                (cons (concat "PYTHONPATH="
@@ -1792,10 +1807,9 @@ This is a no-op if `python-check-comint-prompt' returns nil."
          (kill-local-variable 'python-preoutput-result))))))
 
 (defun python-check-comint-prompt (&optional proc)
-  "Return non-nil iff there's a normal prompt in the inferior buffer.
-If there isn't, it's probably not appropriate to send input to return
-Eldoc information etc.  If PROC is non-nil, check the buffer for that
-process."
+  "Return non-nil if and only if there's a normal prompt in the inferior buffer.
+If there isn't, it's probably not appropriate to send input to return Eldoc
+information etc.  If PROC is non-nil, check the buffer for that process."
   (with-current-buffer (process-buffer (or proc (python-proc)))
     (save-excursion
       (save-match-data (re-search-backward ">>> \\=" nil t)))))
@@ -2188,39 +2202,24 @@ Interactively, prompt for name."
 \f
 ;;;; Skeletons
 
-(defcustom python-use-skeletons nil
-  "Non-nil means template skeletons will be automagically inserted.
-This happens when pressing \"if<SPACE>\", for example, to prompt for
-the if condition."
-  :type 'boolean
-  :group 'python)
-
-(defvar python-skeletons nil
-  "Alist of named skeletons for Python mode.
-Elements are of the form (NAME . EXPANDER-FUNCTION).")
-
 (define-abbrev-table 'python-mode-abbrev-table ()
-  "Abbrev table for Python mode.
-The default contents correspond to the elements of `python-skeletons'."
-  ;; Allow / in abbrevs.
-  :regexp "\\<\\([[:word:]/]+\\)\\W*")
+  "Abbrev table for Python mode."
+  :case-fixed t
+  ;; Allow / inside abbrevs.
+  :regexp "\\(?:^\\|[^/]\\)\\<\\([[:word:]/]+\\)\\W*"
+  ;; Only expand in code.
+  :enable-function (lambda () (not (python-in-string/comment))))
 
 (eval-when-compile
-  ;; Define a user-level skeleton and add it to `python-skeletons' and
-  ;; the abbrev table.
+  ;; Define a user-level skeleton and add it to the abbrev table.
 (defmacro def-python-skeleton (name &rest elements)
   (let* ((name (symbol-name name))
         (function (intern (concat "python-insert-" name))))
     `(progn
-       (add-to-list 'python-skeletons ',(cons name function))
        ;; Usual technique for inserting a skeleton, but expand
        ;; to the original abbrev instead if in a comment or string.
        (define-abbrev python-mode-abbrev-table ,name ""
-        ;; Quote this to give a readable abbrev table.
-        '(lambda ()
-           (if (python-in-string/comment)
-               (insert ,name)
-             (,function)))
+        ',function
         nil t)                         ; system abbrev
        (define-skeleton ,function
         ,(format "Insert Python \"%s\" template." name)
@@ -2313,13 +2312,14 @@ Interactively, prompt for the name with completion."
   (interactive
    (list (completing-read (format "Template to expand (default %s): "
                                  python-default-template)
-                         python-skeletons nil t)))
+                         python-mode-abbrev-table nil t nil nil
+                          python-default-template)))
   (if (equal "" name)
       (setq name python-default-template)
     (setq python-default-template name))
-  (let ((func (cdr (assoc name python-skeletons))))
-    (if func
-       (funcall func)
+  (let ((sym (abbrev-symbol name python-mode-abbrev-table)))
+    (if sym
+        (abbrev-insert sym)
       (error "Undefined template: %s" name))))
 \f
 ;;;; Bicycle Repair Man support
@@ -2375,26 +2375,14 @@ without confirmation."
 \f
 ;;;; Modes.
 
+;; pdb tracking is alert once this file is loaded, but takes no action if
+;; `python-pdbtrack-do-tracking-p' is nil.
+(add-hook 'comint-output-filter-functions 'python-pdbtrack-track-stack-file)
+
 (defvar outline-heading-end-regexp)
 (defvar eldoc-documentation-function)
 (defvar python-mode-running)            ;Dynamically scoped var.
 
-;; Stuff to allow expanding abbrevs with non-word constituents.
-(defun python-abbrev-pc-hook ()
-  "Reset the syntax table after possibly expanding abbrevs."
-  (remove-hook 'post-command-hook 'python-abbrev-pc-hook t)
-  (set-syntax-table python-mode-syntax-table))
-
-(defvar python-abbrev-syntax-table
-  (copy-syntax-table python-mode-syntax-table)
-  "Syntax table used when expanding abbrevs.")
-
-(defun python-pea-hook ()
-  "Set the syntax table before possibly expanding abbrevs."
-  (set-syntax-table python-abbrev-syntax-table)
-  (add-hook 'post-command-hook 'python-abbrev-pc-hook nil t))
-(modify-syntax-entry ?/ "w" python-abbrev-syntax-table)
-
 ;;;###autoload
 (define-derived-mode python-mode fundamental-mode "Python"
   "Major mode for editing Python files.
@@ -2485,7 +2473,6 @@ with skeleton expansions for compound statement templates.
        '((< '(backward-delete-char-untabify (min python-indent
                                                 (current-column))))
         (^ '(- (1+ (current-indentation))))))
-  (add-hook 'pre-abbrev-expand-hook 'python-pea-hook nil t)
   (if (featurep 'hippie-exp)
       (set (make-local-variable 'hippie-expand-try-functions-list)
           (cons 'symbol-completion-try-complete
@@ -2529,162 +2516,6 @@ Runs `jython-mode-hook' after `python-mode-hook'."
 
 ;; pdbtrack features
 
-(defsubst python-point (position)
-  "Returns the value of point at certain commonly referenced POSITIONs.
-POSITION can be one of the following symbols:
-
-  bol  -- beginning of line
-  eol  -- end of line
-  bod  -- beginning of def or class
-  eod  -- end of def or class
-  bob  -- beginning of buffer
-  eob  -- end of buffer
-  boi  -- back to indentation
-  bos  -- beginning of statement
-
-This function does not modify point or mark."
-  (let ((here (point)))
-    (cond
-     ((eq position 'bol) (beginning-of-line))
-     ((eq position 'eol) (end-of-line))
-     ((eq position 'bod) (python-beginning-of-def-or-class))
-     ((eq position 'eod) (python-end-of-def-or-class))
-     ;; Kind of funny, I know, but useful for python-up-exception.
-     ((eq position 'bob) (goto-char (point-min)))
-     ((eq position 'eob) (goto-char (point-max)))
-     ((eq position 'boi) (back-to-indentation))
-     ((eq position 'bos) (python-goto-initial-line))
-     (t (error "Unknown buffer position requested: %s" position)))
-    (prog1
-       (point)
-      (goto-char here))))
-
-(defun python-end-of-def-or-class (&optional class count)
-  "Move point beyond end of `def' or `class' body.
-
-By default, looks for an appropriate `def'.  If you supply a prefix
-arg, looks for a `class' instead.  The docs below assume the `def'
-case; just substitute `class' for `def' for the other case.
-Programmatically, if CLASS is `either', then moves to either `class'
-or `def'.
-
-When second optional argument is given programmatically, move to the
-COUNTth end of `def'.
-
-If point is in a `def' statement already, this is the `def' we use.
-
-Else, if the `def' found by `\\[python-beginning-of-def-or-class]'
-contains the statement you started on, that's the `def' we use.
-
-Otherwise, we search forward for the closest following `def', and use that.
-
-If a `def' can be found by these rules, point is moved to the start of
-the line immediately following the `def' block, and the position of the
-start of the `def' is returned.
-
-Else point is moved to the end of the buffer, and nil is returned.
-
-Note that doing this command repeatedly will take you closer to the
-end of the buffer each time.
-
-To mark the current `def', see `\\[python-mark-def-or-class]'."
-  (interactive "P")                    ; raw prefix arg
-  (if (and count (/= count 1))
-      (python-beginning-of-def-or-class (- 1 count)))
-  (let ((start (progn (python-goto-initial-line) (point)))
-       (which (cond ((eq class 'either) "\\(class\\|def\\)")
-                    (class "class")
-                    (t "def")))
-       (state 'not-found))
-    ;; move point to start of appropriate def/class
-    (if (looking-at (concat "[ \t]*" which "\\>")) ; already on one
-       (setq state 'at-beginning)
-      ;; else see if python-beginning-of-def-or-class hits container
-      (if (and (python-beginning-of-def-or-class class)
-              (progn (python-goto-beyond-block)
-                     (> (point) start)))
-         (setq state 'at-end)
-       ;; else search forward
-       (goto-char start)
-       (if (re-search-forward (concat "^[ \t]*" which "\\>") nil 'move)
-           (progn (setq state 'at-beginning)
-                  (beginning-of-line)))))
-    (cond
-     ((eq state 'at-beginning) (python-goto-beyond-block) t)
-     ((eq state 'at-end) t)
-     ((eq state 'not-found) nil)
-     (t (error "Internal error in `python-end-of-def-or-class'")))))
-
-(defun python-beginning-of-def-or-class (&optional class count)
-  "Move point to start of `def' or `class'.
-
-Searches back for the closest preceding `def'.  If you supply a prefix
-arg, looks for a `class' instead.  The docs below assume the `def'
-case; just substitute `class' for `def' for the other case.
-Programmatically, if CLASS is `either', then moves to either `class'
-or `def'.
-
-When second optional argument is given programmatically, move to the
-COUNTth start of `def'.
-
-If point is in a `def' statement already, and after the `d', simply
-moves point to the start of the statement.
-
-Otherwise (i.e. when point is not in a `def' statement, or at or
-before the `d' of a `def' statement), searches for the closest
-preceding `def' statement, and leaves point at its start.  If no such
-statement can be found, leaves point at the start of the buffer.
-
-Returns t iff a `def' statement is found by these rules.
-
-Note that doing this command repeatedly will take you closer to the
-start of the buffer each time.
-
-To mark the current `def', see `\\[python-mark-def-or-class]'."
-  (interactive "P")                    ; raw prefix arg
-  (setq count (or count 1))
-  (let ((at-or-before-p (<= (current-column) (current-indentation)))
-       (start-of-line (goto-char (python-point 'bol)))
-       (start-of-stmt (goto-char (python-point 'bos)))
-       (start-re (cond ((eq class 'either) "^[ \t]*\\(class\\|def\\)\\>")
-                       (class "^[ \t]*class\\>")
-                       (t "^[ \t]*def\\>"))))
-    ;; searching backward
-    (if (and (< 0 count)
-            (or (/= start-of-stmt start-of-line)
-                (not at-or-before-p)))
-       (end-of-line))
-    ;; search forward
-    (if (and (> 0 count)
-            (zerop (current-column))
-            (looking-at start-re))
-       (end-of-line))
-    (if (re-search-backward start-re nil 'move count)
-       (goto-char (match-beginning 0)))))
-
-(defun python-goto-initial-line ()
-  "Go to the initial line of the current statement.
-Usually this is the line we're on, but if we're on the 2nd or
-following lines of a continuation block, we need to go up to the first
-line of the block."
-  ;; Tricky: We want to avoid quadratic-time behavior for long
-  ;; continued blocks, whether of the backslash or open-bracket
-  ;; varieties, or a mix of the two.  The following manages to do that
-  ;; in the usual cases.
-  ;;
-  ;; Also, if we're sitting inside a triple quoted string, this will
-  ;; drop us at the line that begins the string.
-  (let (open-bracket-pos)
-    (while (python-continuation-line-p)
-      (beginning-of-line)
-      (if (python-backslash-continuation-line-p)
-         (while (python-backslash-continuation-line-p)
-           (forward-line -1))
-       ;; else zip out of nested brackets/braces/parens
-       (while (setq open-bracket-pos (python-nesting-level))
-         (goto-char open-bracket-pos)))))
-  (beginning-of-line))
-
 (defun python-comint-output-filter-function (string)
   "Watch output for Python prompt and exec next file waiting in queue.
 This function is appropriate for `comint-output-filter-functions'."
@@ -2693,24 +2524,26 @@ This function is appropriate for `comint-output-filter-functions'."
                 (and (>= (length string) 5)
                      (string-equal (substring string -5) "\n>>> ")))
             python-file-queue)
-    (python-safe (delete-file (car python-file-queue)))
+    (condition-case nil
+        (delete-file (car python-file-queue))
+      (error nil))
     (setq python-file-queue (cdr python-file-queue))
     (if python-file-queue
        (let ((pyproc (get-buffer-process (current-buffer))))
          (python-execute-file pyproc (car python-file-queue))))))
 
 (defun python-pdbtrack-overlay-arrow (activation)
-  "Activate or de arrow at beginning-of-line in current buffer."
-  ;; This was derived/simplified from edebug-overlay-arrow
-  (cond (activation
-        (setq overlay-arrow-position (make-marker))
-        (setq overlay-arrow-string "=>")
-        (set-marker overlay-arrow-position
-                    (python-point 'bol) (current-buffer))
-        (setq python-pdbtrack-is-tracking-p t))
-       (python-pdbtrack-is-tracking-p
-        (setq overlay-arrow-position nil)
-        (setq python-pdbtrack-is-tracking-p nil))))
+  "Activate or deactivate arrow at beginning-of-line in current buffer."
+  (if activation
+      (progn
+        (setq overlay-arrow-position (make-marker)
+              overlay-arrow-string "=>"
+              python-pdbtrack-is-tracking-p t)
+        (set-marker overlay-arrow-position
+                    (save-excursion (beginning-of-line) (point))
+                    (current-buffer)))
+    (setq overlay-arrow-position nil
+          python-pdbtrack-is-tracking-p nil)))
 
 (defun python-pdbtrack-track-stack-file (text)
   "Show the file indicated by the pdb stack entry line, in a separate window.
@@ -2718,49 +2551,131 @@ This function is appropriate for `comint-output-filter-functions'."
 Activity is disabled if the buffer-local variable
 `python-pdbtrack-do-tracking-p' is nil.
 
-We depend on the pdb input prompt matching `python-pdbtrack-input-prompt'
-at the beginning of the line."
+We depend on the pdb input prompt being a match for
+`python-pdbtrack-input-prompt'.
+
+If the traceback target file path is invalid, we look for the
+most recently visited python-mode buffer which either has the
+name of the current function or class, or which defines the
+function or class.  This is to provide for scripts not in the
+local filesytem (e.g., Zope's 'Script \(Python)', but it's not
+Zope specific).  If you put a copy of the script in a buffer
+named for the script and activate python-mode, then pdbtrack will
+find it."
   ;; Instead of trying to piece things together from partial text
   ;; (which can be almost useless depending on Emacs version), we
   ;; monitor to the point where we have the next pdb prompt, and then
   ;; check all text from comint-last-input-end to process-mark.
   ;;
-  ;; KLM: It might be nice to provide an optional override, so this
-  ;; routine could be fed debugger output strings as the text
-  ;; argument, for deliberate application elsewhere.
-  ;;
-  ;; KLM: We're very conservative about clearing the overlay arrow, to
-  ;; minimize residue.  This means, for instance, that executing other
-  ;; pdb commands wipes out the highlight.
+  ;; Also, we're very conservative about clearing the overlay arrow,
+  ;; to minimize residue.  This means, for instance, that executing
+  ;; other pdb commands wipe out the highlight.  You can always do a
+  ;; 'where' (aka 'w') PDB command to reveal the overlay arrow.
+
   (let* ((origbuf (current-buffer))
         (currproc (get-buffer-process origbuf)))
+
     (if (not (and currproc python-pdbtrack-do-tracking-p))
         (python-pdbtrack-overlay-arrow nil)
-      (let* (;(origdir default-directory)
-             (procmark (process-mark currproc))
+
+      (let* ((procmark (process-mark currproc))
              (block (buffer-substring (max comint-last-input-end
                                            (- procmark
                                               python-pdbtrack-track-range))
                                       procmark))
-             fname lineno)
+             target target_fname target_lineno target_buffer)
+
         (if (not (string-match (concat python-pdbtrack-input-prompt "$") block))
             (python-pdbtrack-overlay-arrow nil)
-          (if (not (string-match
-                    (concat ".*" python-pdbtrack-stack-entry-regexp ".*")
-                    block))
-              (python-pdbtrack-overlay-arrow nil)
-            (setq fname (match-string 1 block)
-                  lineno (match-string 2 block))
-            (if (file-exists-p fname)
-                (progn
-                  (find-file-other-window fname)
-                  (goto-line (string-to-number lineno))
-                  (message "pdbtrack: line %s, file %s" lineno fname)
-                  (python-pdbtrack-overlay-arrow t)
-                  (pop-to-buffer origbuf t) )
-              (if (= (elt fname 0) ?\<)
-                  (message "pdbtrack: (Non-file source: '%s')" fname)
-                (message "pdbtrack: File not found: %s" fname)))))))))
+
+          (setq target (python-pdbtrack-get-source-buffer block))
+
+          (if (stringp target)
+              (progn
+                (python-pdbtrack-overlay-arrow nil)
+                (message "pdbtrack: %s" target))
+
+            (setq target_lineno (car target)
+                  target_buffer (cadr target)
+                  target_fname (buffer-file-name target_buffer))
+            (switch-to-buffer-other-window target_buffer)
+            (goto-line target_lineno)
+            (message "pdbtrack: line %s, file %s" target_lineno target_fname)
+            (python-pdbtrack-overlay-arrow t)
+            (pop-to-buffer origbuf t)
+            ;; in large shell buffers, above stuff may cause point to lag output
+            (goto-char procmark)
+            )))))
+  )
+
+(defun python-pdbtrack-get-source-buffer (block)
+  "Return line number and buffer of code indicated by block's traceback text.
+
+We look first to visit the file indicated in the trace.
+
+Failing that, we look for the most recently visited python-mode buffer
+with the same name or having the named function.
+
+If we're unable find the source code we return a string describing the
+problem."
+
+  (if (not (string-match python-pdbtrack-stack-entry-regexp block))
+
+      "Traceback cue not found"
+
+    (let* ((filename (match-string 1 block))
+           (lineno (string-to-number (match-string 2 block)))
+           (funcname (match-string 3 block))
+           funcbuffer)
+
+      (cond ((file-exists-p filename)
+             (list lineno (find-file-noselect filename)))
+
+            ((setq funcbuffer (python-pdbtrack-grub-for-buffer funcname lineno))
+             (if (string-match "/Script (Python)$" filename)
+                 ;; Add in number of lines for leading '##' comments:
+                 (setq lineno
+                       (+ lineno
+                          (save-excursion
+                            (set-buffer funcbuffer)
+                            (if (equal (point-min)(point-max))
+                                0
+                              (count-lines
+                               (point-min)
+                               (max (point-min)
+                                    (string-match "^\\([^#]\\|#[^#]\\|#$\\)"
+                                                  (buffer-substring
+                                                   (point-min) (point-max)))
+                                    )))))))
+               (list lineno funcbuffer))
+
+            ((= (elt filename 0) ?\<)
+             (format "(Non-file source: '%s')" filename))
+
+            (t (format "Not found: %s(), %s" funcname filename)))
+      )
+    )
+  )
+
+(defun python-pdbtrack-grub-for-buffer (funcname lineno)
+  "Find recent python-mode buffer named, or having function named funcname."
+  (let ((buffers (buffer-list))
+        buf
+        got)
+    (while (and buffers (not got))
+      (setq buf (car buffers)
+            buffers (cdr buffers))
+      (if (and (save-excursion (set-buffer buf)
+                               (string= major-mode "python-mode"))
+               (or (string-match funcname (buffer-name buf))
+                   (string-match (concat "^\\s-*\\(def\\|class\\)\\s-+"
+                                         funcname "\\s-*(")
+                                 (save-excursion
+                                   (set-buffer buf)
+                                   (buffer-substring (point-min)
+                                                     (point-max))))))
+          (setq got buf)))
+    got))
 
 (defun python-toggle-shells (arg)
   "Toggles between the CPython and JPython shells.
@@ -2801,6 +2716,26 @@ Programmatically, ARG can also be one of the symbols `cpython' or
            mode-name "JPython")))
     (message "Using the %s shell" msg)))
 
+;; Python subprocess utilities and filters
+(defun python-execute-file (proc filename)
+  "Send to Python interpreter process PROC \"execfile('FILENAME')\".
+Make that process's buffer visible and force display.  Also make
+comint believe the user typed this string so that
+`kill-output-from-shell' does The Right Thing."
+  (let ((curbuf (current-buffer))
+       (procbuf (process-buffer proc))
+;      (comint-scroll-to-bottom-on-output t)
+       (msg (format "## working on region in file %s...\n" filename))
+        ;; add some comment, so that we can filter it out of history
+       (cmd (format "execfile(r'%s') # PYTHON-MODE\n" filename)))
+    (unwind-protect
+       (save-excursion
+         (set-buffer procbuf)
+         (goto-char (point-max))
+         (move-marker (process-mark proc) (point))
+         (funcall (process-filter proc) proc msg))
+      (set-buffer curbuf))
+    (process-send-string proc cmd)))
 ;;;###autoload
 (defun python-shell (&optional argprompt)
   "Start an interactive Python interpreter in another window.
@@ -2857,14 +2792,12 @@ filter."
     (switch-to-buffer-other-window
      (apply 'make-comint python-which-bufname python-which-shell nil args))
     (make-local-variable 'comint-prompt-regexp)
-    (set-process-sentinel (get-buffer-process (current-buffer)) 'python-sentinel)
+    (set-process-sentinel (get-buffer-process (current-buffer))
+                          'python-sentinel)
     (setq comint-prompt-regexp "^>>> \\|^[.][.][.] \\|^(pdb) ")
     (add-hook 'comint-output-filter-functions
              'python-comint-output-filter-function nil t)
     ;; pdbtrack
-    (add-hook 'comint-output-filter-functions
-             'python-pdbtrack-track-stack-file nil t)
-    (setq python-pdbtrack-do-tracking-p t)
     (set-syntax-table python-mode-syntax-table)
     (use-local-map python-shell-map)))