5

I want to generate a string in an exception handler that contains the name of the exception, and any arguments passed...oretty much the final output that one gets with Traceback.

For example, if raise bar.FnordError("message") is called, in the exception handler, I want to produce the string: "bar.FnordError: message"

I want it to work for built in exceptions, as well as exceptions in the current and other modules. This is what I came up with, but it doesn't seem very pythonic.

def this_is_lame(err):
    if type(err).__module__ in ['__main__', 'builtins']:
        return "{}: {}".format(type(err).__name__, err)
    else:
        return "{}.{}: {}".format(type(err).__module__, type(err).__name__, err)

I've dug through the BaseException C code in python, and the Traceback standard library. I seem to be missing the correct "nice" accessor.

Is BaseException.__format__ documented anywhere? Are there escapes for it?

I've played around in the interpreter and nothing quite gives me what I want.

import sys
import traceback

import bar

try:
    raise bar.FnordError("message")
except Exception as err:
    print(type(err))
    print(repr(err))
    print(type(err).__module__)
    print(type(err).__name__)
    print(err)
    print("this is what I want: '{}'".format(this_is_lame(err)))

print()

try:
    raise ValueError("message")
except Exception as err:
    print(type(err))
    print(repr(err))
    print(type(err).__module__)
    print(type(err).__name__)
    print("this is what I want: '{}'".format(this_is_lame(err)))

which produces:

$ python foo.py
<class 'bar.FnordError'>
FnordError('message',)
bar
FnordError
message
this is what I want: 'bar.FnordError: message'

<class 'ValueError'>
ValueError('message',)
builtins
ValueError
this is what I want: 'ValueError: message'
1
  • I think your this_is_lame_too function is actually pretty nifty. Useful for figuring out someone's code. Seems good to me. Commented Apr 28, 2016 at 17:47

1 Answer 1

2

There's no "nice" accessor. Python itself does something pretty similar to what you're doing in the default sys.excepthook, although exceptions defined in __main__ print as __main__.WhateverException.

If you want to see how Python itself does it, on Python 2, the check happens in PyErr_Display, which checks strcmp(modstr, "exceptions"):

moduleName = PyObject_GetAttrString(exception, "__module__");
if (moduleName == NULL)
    err = PyFile_WriteString("<unknown>", f);
else {
    char* modstr = PyString_AsString(moduleName);
    if (modstr && strcmp(modstr, "exceptions"))
    {
        err = PyFile_WriteString(modstr, f);
        err += PyFile_WriteString(".", f);
    }
    Py_DECREF(moduleName);
}

On Python 3, print_exception checks _PyUnicode_CompareWithId(moduleName, &PyId_builtins).

moduleName = _PyObject_GetAttrId(type, &PyId___module__);
if (moduleName == NULL || !PyUnicode_Check(moduleName))
{
    Py_XDECREF(moduleName);
    err = PyFile_WriteString("<unknown>", f);
}
else {
    if (_PyUnicode_CompareWithId(moduleName, &PyId_builtins) != 0)
    {
        err = PyFile_WriteObject(moduleName, f, Py_PRINT_RAW);
        err += PyFile_WriteString(".", f);
    }
    Py_DECREF(moduleName);
}
Sign up to request clarification or add additional context in comments.

2 Comments

What is PyId_builtins? Where is this defined?
There is a macro in Python.h: _Py_IDENTIFIER(foo) which initialises a structure named PyId_foo containing the string "foo". Hence we see variables such as PyId_builtins/PyId___builtins__.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.