13

Note: I am asking this question after researching how to actually do it. Other questions which are somewhat similar, but actually differ from my question relate to:

  1. Color-coding within the python script
  2. Importing color libraries within the script
  3. Using tools like Solarized to improve tools like Vim to color-code python code.

What I am looking for: I already have a simple python script (say, test.py) and I am executing it from the Gnome terminal.

python test.py

test.py will output some errors right in the terminal. Error example:

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    with open('', 'rb') as csvfile:
IOError: [Errno 2] No such file or directory: ''

I want the error keywords to be in bold red for example, line numbers in a specific color amongst others, such that it is easy to track down the errors.

As you can see, Stackoverflow already color-codes the error nicely. On my Gnome-terminal, the output is uni-color. How can I get such a nice color-coded output on the terminal?

S.B
  • 13,077
  • 10
  • 22
  • 49
Shailen
  • 7,909
  • 3
  • 29
  • 37

4 Answers4

14

You could use the IPython coloring. Just put this at the beginning of your program. Every exception will be then handled by ultratb and be displayed in color and also show the locals() values for the code snippet that made the exception.

import sys
from IPython.core import ultratb
sys.excepthook = ultratb.FormattedTB(color_scheme='Linux', call_pdb=False)

This will work even if you invoke your script with the vanilla python interpreter.

Harsh Verma
  • 529
  • 6
  • 10
Dorian B.
  • 1,101
  • 13
  • 22
  • 1
    `mode='Verbose'` option can be an overkill because it can produces way too much output when the error originates in a function that is nested deep in the call stack. The default mode(`mode='Plain'`) seems sufficient to answer the question asked. – Harsh Verma Jan 14 '23 at 13:13
6

[Note: Colors updated 7 Dec 2021 to be more legible :)]

Note: Doesn't directly answer the question above, since I'm bad at sed. But this improves readability, and compared to the others which require ipython/are slow, this is what I actually use regularly.

Current (lightweight solution -- as in, don't need to install anything, and don't have to edit existing python files one-by-one)

As per, https://stackoverflow.com/a/20910449, let's use sed and ANSI color codes.

Add to ~/.bashrc

norm="$(printf '\033[0m')" #returns to "normal"
bold="$(printf '\033[0;1m')" #set bold
red="$(printf '\033[0;31m')" #set red
boldyellowonblue="$(printf '\033[0;1;33;44m')" 
boldyellow="$(printf '\033[0;1;33m')"
boldred="$(printf '\033[0;1;31m')" #set bold, and set red.

copython() {
        python $@ 2>&1 | sed -e "s/Traceback/${boldyellowonblue}&${norm}/g" \
        -e "s/File \".*\.py\".*$/${boldyellow}&${norm}/g" \
        -e "s/\, line [[:digit:]]\+/${boldred}&${norm}/g"
    }

Reload

$ source ~/.bashrc

What it looks like originally

$ python main.py

not colored python traceback in terminal

Pretty annoying to read. Now compare with

$ copython main.py

[colored python traceback in termina

It's a bit garish but I'm happy with it, and I don't understand ANSI codes so these are the colors I'm stuck with :)


Colors updated to be more pleasing [7 Dec 2021]

# ---
# Color python error output
# ---

norm="$(printf '\033[0m')" #returns to "normal"
bold="$(printf '\033[0;1m')" #set bold
red="$(printf '\033[0;31m')" #set red
boldyellowonblue="$(printf '\033[0;1;33;44m')" #set blue bkgrd, bold yellow text
boldyellow="$(printf '\033[0;1;33m')" #set gold yellow text
boldred="$(printf '\033[0;1;31m')" #set bold red

# Color error messages from python, use as "copython test.py" instead of "python test.py"
# WARNING: Takes time to run (small but noticeable)
# May also interfer with print to console (for long running programs) 
copython() {
    python $@ 2>&1 | sed -e "s/Traceback/${boldyellowonblue}&${norm}/g" \
        -e "s/File \".*\.py\".*$/${bold}&${norm}/g" \
        -re "s/\, line [0-9]\+/${boldred}&${norm}/g" \
        -re "s/ {4}(.*)$/${boldyellow}&${norm}/g" \ 
        -e "s/.*Error:.*$/${boldred}&${norm}/g" \
    }

colored python output


Note on ANSI Color Code

Note that \033[ marks the beginning of a code, which helped me understand a bit better what is going on.

0m # normal
0;1m # bold 
0;1;33;44m # bold yellow on blue 
0;1;33m # bold yellow 
0;1;31m # bold red

So I guess the first 1 indicates bold, then the second 33 indicates foreground color, and the third 44 indicates background color. Something like that.


Obsolete

I was also looking for something where I didn't need to modify every single python file I want to print error messages from. How to use Tobin's answer wasn't immediately clear to me.

For those wondering how to use, need to install vimcat, then add above function to bashrc (or other file that you source), and run $ colorized test.py (instead of $ python test.py)

For instance

~$ wget https://raw.githubusercontent.com/vim-scripts/vimcat/master/vimcat vimcat
~$ mv vimcat /usr/share/bin  # or /home/bin or wherever you want
~$ echo $PATH # make sure vimcat's directory is in your PATH variable, if not add to `~/.bashrc`
home/rui/.local/bin:/usr/local/sbin:/usr/share/bin
~$ source ~/.bashrc # reload PATH if had to add vimcat location
~$ vimcat somefile.sh # test that vimcat can be called
~$ colorized calibrate.py 

What before and after looks like for me: before and after

And verifying that vimcat works / has been sourced properly:

vimcat

Note that this does take noticeably more time to run!

$ time colorized calibrate.py 
real    0m0.484s
user    0m0.392s
sys 0m0.085s

rui@chaiX1YG2:~$ $ time python calibrate.py 
real    0m0.343s
user    0m0.271s
sys 0m0.072s
orangenarwhals
  • 365
  • 1
  • 5
  • 18
1

Quick hack solution, UNIX only. Redirect stdout to a file with .py suffix. Then display file using vimcat for colorised output. Wrap this all up in a shell function. For example in bash;

# print colorised python output
colorized() {
    local out='out.py'
    if (( $# < 1)) 
    then
        printf "Usage: %s pyhon-script\n" $0 >&2
        return 1;
    fi
    if [ -e $out ]; 
    then
        rm $out
    fi
    python $@ 2> $out
    empty=$(stat $out | grep empty)
    if (( $? == 1 ))
    then
        vimcat $out
    fi
}
Tobin
  • 359
  • 1
  • 2
  • 14
0

FWIW, you can wrap the script in a main function, and call the main function within a try ... except block, get the error message, colourize it and print it;

To get the error message you need a call to sys.exc_info. traceback.format_exception formats the stack-trace and the exception information. Using basic regex you can wrap every ..Err.. inside a \033[91m...Err...\033[0m which turns the colour into red:

def main():
   with open('xxx.txt', 'r') as fin:
        return fin.read()

try:
    main()
except:
    import re
    from sys import exc_info
    from traceback import format_exception

    RED, REV = r'\033[91m', r'\033[0m'
    err = ''.join(format_exception(*exc_info()))
    print(re.sub(r'(\w*Err\w*)', RED + r'\1' + REV, err))

Result:

err

behzad.nouri
  • 74,723
  • 18
  • 126
  • 124