23

Say I have a python script with a runtime error:

$ cat example.py  
#! /usr/bin/env python3

a = 1/0

which gives:

$ python3 example.py 
Traceback (most recent call last):
  File "example.py", line 3, in <module>
    a = 1/0
ZeroDivisionError: division by zero

I want Vim to jump to the problematic line of that file (line 3 in this case). I know Vim can do this because it works just fine for catching errors at compile time in C with gcc using :make and the quickfix window.

quickfix output from gcc

Sure, I can populate Vim's quickfix window with :set makeprg=python3\ % and then :make, but it does not jump to the line number where the traceback points to. When I look in :copen it just highlights the first line of the trace, and I can't jump to the relevant line number.

quickfix output from python3

(I'm using Vim 7.4 on Debian jessie in case that matters.)

My questions are:

  • Can I configure Vim so that it knows how to get the relevant line number from a Python traceback?

  • Can I modify the Python interpreter to spit out an error format that Vim already knows how to parse and get the relevant line number?

3
  • You could subclass the logger in your script to produce traceback points one per line (see here to get started), then adjust errorformat accordingly and write a compiler plugin for Vim (see :help :compiler and :help write-compiler-plugin). Probably not worth the effort if you don't know exactly what you're doing and you're not enthusiastic enough to dig everything out from the docs. Oct 15, 2015 at 17:01
  • 1
    I asked a similar question on StackOverflow, you may find those answers useful stackoverflow.com/questions/11333112/…
    – jalanb
    Oct 16, 2015 at 0:24
  • 1
    Reddit View Python traceback as quickfix recommends Tim Pope's Vim-Dispatch package. :Dispatch python3 % will return the output of the current script as a quick fix window. Feb 20 at 16:16

4 Answers 4

9

Vim comes with a set of "compiler" scripts, one of which is called "pyunit". If you run :compiler pyunit and then :make (with your suggested value for 'makeprg'), quickfix is populated as you expect. However, it only works well if there's one level to the stack trace.

Improving that compiler script would be a useful exercise.

The unstack plugin may be of interest, as it provides a general mechanism to parse and view the locations reported in a stack trace and has Python support built in.

14

Built in compiler plugin pyunit

As already suggested by jamessan, one option is to use the built in compiler plugin pyunit:

:compiler pyunit
:set makeprg=python3\ %
:make

This has the downside, that it collapses the stack trace to a single error message. For example the following python script:

def lumberjack():
    bright_side_of_death()

def bright_side_of_death():
    return tuple()[0]

lumberjack()

... produces this error message:

|| Traceback (most recent call last):
lumberjack.py|7|  IndexError: tuple index out of range

Writing your own compiler plugin

As an alternative, you can provide your own compiler plugin in ~/.vim/compiler/python.vim:

if exists("current_compiler")
  finish
endif
let current_compiler = "python"

let s:cpo_save = &cpo
set cpo&vim

CompilerSet errorformat=
      \%*\\sFile\ \"%f\"\\,\ line\ %l\\,\ %m,
      \%*\\sFile\ \"%f\"\\,\ line\ %l,
CompilerSet makeprg=python3\ %

let &cpo = s:cpo_save
unlet s:cpo_save

Select the plugin manually with :compiler python, or load it automatically by adding this to ~/.vim/after/ftplugin/python.vim:

if !exists("current_compiler")
  compiler python
endif

With the python script from above, Vim populates the quickfix window with:

|| Traceback (most recent call last):
lumberjack.py|7| in <module>
||     lumberjack()
lumberjack.py|2| in lumberjack
||     bright_side_of_death()
lumberjack.py|5| in bright_side_of_death
||     return tuple()[0]
|| IndexError: tuple index out of range

See :help write-compiler-plugin for more information.

2
  • What's up with the cpo_save?
    – CervEd
    Jun 19 at 8:01
  • @CervEd cpo_save and set cpo&vim is boilerplate code used in most plugins. It's there to disable Vi compatibility for the duration of this script. For example a user might have line continuation disabled (:help cpo-C), so this plugin would fail when setting errorformat.
    – siho
    Jun 19 at 22:01
3

quickfix.py parses the traceback into a vim-friendly errorformat. Here is an example of running it on a file with a single line 1 / 0.

❯❯❯ quickfix.py tests/errors/div_by_zero.py
"tests/errors/div_by_zero.py":1: ZeroDivisionError: division by zero

By default, it shows user files, but it can show system files too (running it on a file containing import os; os.environ['123']):

❯❯❯ quickfix.py -a /tmp/test.py                                                                                                        
"/usr/lib/lib/python3.7/os.py":678: KeyError: '123'
"/tmp/test.py":1: in function <module>

Configuration:

When quickfix.py is available in the current path, add the following lines in the vimrc to use it.

if has("autocmd")
  autocmd FileType python setlocal makeprg=quickfix.py\ %
  autocmd FileType python setlocal errorformat=%E\"%f\":%l:%m,
endif
0

Not an automatic method but the python Traceback states the line number --- 3 in your example --- and so invoking vim:

$ vim +3 example.py

will open the example.py with the cursor on the third line.

1
  • 2
    I'm aware of that, but this is about Quickfix support. After I've run :make on a file I've already got open, it's faster to use :3 to jump to the third line than to close and re-open. Also, doing this manually is a pain for more complex stack traces, which is why I want Quickfix support. Nov 3, 2016 at 16:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.