views:

182

answers:

3

I'm running pdb on my testcases in Python through the gud buffer. When I get a stacktrace/failure in my testcase, it looks like this:

FAIL: test_foo_function (__main__.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/testfoo.py", line 499, in test_foo_function
    self.assertEqual('foo', 'foo')

I'd love to be able to make the line(s) like:

File "test/testfoo.py", line 499, in test_foo_function

clickable and take be to line 499 in testfoo.py.

(edit) The folks on the python-mode list led me to pdbtrack and I was able to get it to work there. See answer below

A: 

I think what you want to customize is compilation-parse-errors-filename-function, which is a function that takes a filename, and returns a modified version of the filename to be displayed. This is a buffer local variable, so you should set it in each buffer that will be displaying python errors (there is probably an appropriate hook to use, I do not have python mode installed so I cannot look it up). You would use propertize to return a version of the input filename that acts as a hyperlink to load the actual file. propertize is well documented in the elisp manual.

If compilation-parse-errors-filename-function is not getting called, then you want to add a list to compilation-error-regexp-alist-alist (that does say alist-alist, that is not a typo) which is a list of mode names followed by regular expressions to match errors, and numerical indexes of the matching line number, filename etc. info in the error regexp match.

Justin Smith
A: 

Adding to Justin's answer:

I have the following in my slime config which is supposed to jump to a file and line from a clojure stack trace.

Unfortunately I must admit that it doesn't actually work for me at the moment - the function isn't able to find the correct file - but as far as I can tell, that should be fixable by changing how project-root is defined or by changing the structure of my projects on the filesystem (I just haven't had the time or inclination to look into it).

It does bring up a good point though, in most functinality like this it's a bit tricky to figure out the project root in a generic and portable fashion. In this case we rely on a src directory, but that's probably not appropriate for your python projects.

So following on from where Justin left off, you should be able to take some tips from the function below and parse the file name and line numbers from the test case error, create a link to the line number, and use the compilation-parse-errors-filename-function and propertize to make the line in the gud buffer a link.

If you do get it to work, please add an answer to your own question. I think a lot of people would find it useful.

  (defun slime-jump-to-trace (&optional on)
    "Jump to the file/line that the current stack trace line references.
    Only works with files in your project root's src/, not in dependencies."
    (interactive)
    (save-excursion
      (beginning-of-line)
      (search-forward-regexp "[0-9]: \\([^$(]+\\).*?\\([0-9]*\\))")
      (let ((line (string-to-number (match-string 2)))
            (ns-path (split-string (match-string 1) "\\."))
            (project-root (locate-dominating-file default-directory "src/")))

        (find-file (format "%s/src/%s.clj" project-root
                           (mapconcat 'identity ns-path "/")))
        (goto-line line))))

I should also mention that I copied this function from somewhere on the web, but I can't remember the URL. It seems to be from Phil Hagelberg's (technomancy) excellent Emacs starter kit.

liwp
+3  A: 

Thanks to a hint by Gerard B I figured it out. I'm doing this from pdbtrack (shell) instead of pure pdb, but it should work in both I believe. You need to enable compilation-shell-minor-mode. And have the following code in your .emacs:

;; if compilation-shell-minor-mode is on, then these regexes
;; will make errors linkable
(defun matt-add-global-compilation-errors (list)
  (dolist (x list)
    (add-to-list 'compilation-error-regexp-alist (car x))
    (setq compilation-error-regexp-alist-alist
      (cons x
            (assq-delete-all (car x)
                             compilation-error-regexp-alist-alist)))))

(matt-add-global-compilation-errors
 `(
   (matt-python ,(concat "^ *File \\(\"?\\)\\([^,\" \n    <>]+\\)\\1"
                    ", lines? \\([0-9]+\\)-?\\([0-9]+\\)?")
           2 (3 . 4) nil 2 2)
   (matt-pdb-stack ,(concat "^>?[[:space:]]*\\(\\([-_./a-zA-Z0-9 ]+\\)"
                       "(\\([0-9]+\\))\\)"
                       "[_a-zA-Z0-9]+()[[:space:]]*->")
              2 3 nil 0 1)
   (matt-python-unittest-err "^  File \"\\([-_./a-zA-Z0-9 ]+\\)\", line \\([0-9]+\\).*" 1 2)
   )
 )

(defun matt-set-local-compilation-errors (errors)
  "Set the buffer local compilation errors.

Ensures than any symbols given are defined in
compilation-error-regexp-alist-alist."
  (dolist (e errors)
     (when (symbolp e)
      (unless (assoc e compilation-error-regexp-alist-alist)
        (error (concat "Error %s is not listed in "
                       "compilation-error-regexp-alist-alist")
               e))))
  (set (make-local-variable 'compilation-error-regexp-alist)
       errors))

Then you can use standard compile mode navigation to zip through the error stack trace.

matt harrison
Note that this works when the file/buffer is first open. Over time there appears to be some "drift" (ie the cursor appears some lines after the actual line)
matt harrison