8

Is there a way to show only the important directory paths when executing a python program?

Currently I get this:

python3 foo.py                                        
Traceback (most recent call last):
  File "foo.py", line 60, in <module>
    foo = Foo()
  File "foo.py", line 22, in __init__
    self._run()
  File "/media/MyDocuments/xxxxxxx/yyyyyyyyy/python_code/foo.py", line 18, in check_input
    bar = obj.get_action()
AttributeError: 'obj' object has no attribute 'get_action'

As I know in which directory my code is, the full directory makes the error message just worse readable. Can I tell python to show me the output more like this?

python3 foo.py                                        
    Traceback (most recent call last):
      File "foo.py", line 60, in <module>
        foo = Foo()
      File "foo.py", line 22, in __init__
        self._run()
      File ".../foo.py", line 18, in check_input
        bar = obj.get_action()
    AttributeError: 'obj' object has no attribute 'get_action'

Answer

Using the code from unutbu I added some lines for colors, in case someone is looking for an easy improvement of the interpreter output, just use this as a module and import it:

import sys
import traceback
import os
import re

RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
LIGHT_PURPLE = '\033[94m'
PURPLE = '\033[95m'
CYAN = '\033[96m'
END = '\033[0m'

def my_excepthook(type, value, tb):
    lines = traceback.format_list(traceback.extract_tb(tb))
    def shorten(match):
        return 'File "{}"'.format(os.path.basename(match.group(1)))
    lines = [re.sub(r'File "([^"]+)"', shorten, line) for line in lines]
    _print_color(lines)
    # print(''.join(lines))
    print(RED + '{}: {}'.format(type.__name__, value) + END)

sys.excepthook = my_excepthook


def _print_color(lines):
    for l in lines:
        for i in range(len(l)-1):
            if l[i:i+5]=="line ":
                i +=5
                # Find the length of the number
                numLen = 0
                while l[i+numLen].isdigit():
                    numLen +=1

                # Find the length of the function
                funLen = 0
                while not l[i+numLen+4 + funLen]=="\n":
                    funLen+=1

                l = ''.join([l[:i],
                        YELLOW+"{}".format(l[i:i+numLen])+END,
                        l[i+numLen:i+numLen+5],
                        LIGHT_PURPLE+"{}".format(l[i+numLen+5:i+numLen+5+funLen])+END,
                        CYAN+"{}".format(l[i+numLen+5+funLen:])+END])
                print(l,end="")
                break
    print("")
2
  • 1
    Can you provide a short, complete program that produces a message like that? I can't reproduce your results -- all of my tracebacks say "foo.py", just like the first two lines of your traceback.
    – Robᵩ
    Commented May 5, 2016 at 20:54
  • All my code is saved in the same directory, so any example would produce this kind of error message at my computer. I'm using Ubuntu, python3, maybe another OS uses a different standard python interpreter.
    – Natjo
    Commented May 6, 2016 at 8:38

3 Answers 3

5

You could assign a custom function to sys.excepthook to handle all uncaught exceptions:

sys.excepthook = my_excepthook

Then you could use

def my_excepthook(type, value, tb):
    lines = traceback.format_list(traceback.extract_tb(tb))
    # process/modify lines
    print(''.join(lines))

to obtain the traceback error message as a sequence of lines, then modified and printed as you please.


For example, if you wish to shorten all file paths to just its basename, you could use:

import sys
import traceback
import os
import re

def my_excepthook(type, value, tb):
    lines = traceback.format_list(traceback.extract_tb(tb))
    def shorten(match):
        return 'File "{}"'.format(os.path.basename(match.group(1)))
    lines = [re.sub(r'File "([^"]+)"', shorten, line, 1) for line in lines]
    print(''.join(lines))
    print('{}: {}'.format(type.__name__, value))

sys.excepthook = my_excepthook   # comment this out to see the difference

class Foo():
    def run(self):
        1/0

foo = Foo()
foo.run()

which yields

  File "script.py", line 24, in <module>
    foo.run()
  File "script.py", line 21, in run
    1/0

ZeroDivisionError: division by zero

instead of

Traceback (most recent call last):
  File "/home/unutbu/pybin/script.py", line 24, in <module>
    foo.run()
  File "/home/unutbu/pybin/script.py", line 21, in run
    1/0
ZeroDivisionError: division by zero
1
  • 1
    Thank you, overwriting the system method works perfectly. I created my own module with your code. So i just need one line to import this module and have no code overhead. Great!
    – Natjo
    Commented May 6, 2016 at 7:13
0

You can have a try except block around your main procedure or at the topmost level if you don't have a main routine. In the except block you can parse the exception trace to remove the directory name and other non-important information using the traceback module.

import traceback
import sys
if __name__ == '__main__':
    try:
        #SomeOperation
        #More operation
    except:
        errorMsg = traceback.format_exc()
        #Format error message according to your preference
        print(errorMsgTransformed)
        sys.exit(1) 
-1

I think the best way to write custom errors is to use try and except.

try:
    doSomething() # You code goes here.
except Exception: 
    # Whatever you want to be shown, full path, anything.
    pass

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