Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python3: Don't show full directory path on error message

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("")
like image 953
Natjo Avatar asked May 05 '16 17:05

Natjo


People also ask

How do I get the full file path in Python?

To get an absolute path in Python you use the os. path. abspath library. Insert your file name and it will return the full path relative from the working directory including the file.

How do I fix error No such file or directory?

In some cases, this error could be shown when the path of the specified file or folders exceeds 258 characters in length. The way to solve this is to reduce the length of the full path to the items specified, either by moving or renaming the file(s) and/or containing folders.

How do you use absolute path instead of relative path in Python?

Absolute and Relative file paths An absolute file path describes how to access a given file or directory, starting from the root of the file system. A file path is also called a pathname. Relative file paths are notated by a lack of a leading forward slash. For example, example_directory.


1 Answers

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
like image 175
unutbu Avatar answered Oct 04 '22 08:10

unutbu