Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User-defined exception: <unprintable ... object>

I tried to define my own exception class in python 2.7, deriving from BaseException.

class NestedCommentException(BaseException):
    """
    Exception for nested comments
    """
    def __init__(self, file_path, list_lines):
        self.file_path = file_path
        self.list_lines = list_lines

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        return 'File {0} contains nested comments at lines {1}'.format(self.file_path, ', '.join(self.list_lines))

But when throwing it, it cannot be printed: raise NestedCommentException(file_path, list_lines) triggers

Traceback (most recent call last):
  File "D:\DATA\FP12210\My Documents\Outils\SVN\05_impl\2_tools\svn_tag_setup.py", line 85, in <module>
    tag_checks()
  File "D:\DATA\FP12210\My Documents\Outils\SVN\05_impl\2_tools\svn_tag_setup.py", line 66, in tag_checks
    check_nested_comments(ddl_path)
  File "D:\DATA\FP12210\My Documents\Outils\SVN\05_impl\2_tools\svn_tag_setup.py", line 54, in check_nested_comments
    raise NestedCommentException(file_path, list_lines)
NestedCommentException: <unprintable NestedCommentException object>

Can you please explain why this happens, even if I defined __str__ and __repr__ methods ?

like image 290
Emmanuel Avatar asked Oct 26 '12 15:10

Emmanuel


People also ask

How do you catch an instance of a specific exception type?

Catching Specific Exceptions in Python A try clause can have any number of except clauses to handle different exceptions, however, only one will be executed in case an exception occurs. We can use a tuple of values to specify multiple exceptions in an except clause. Here is an example pseudo code.

How do you create a user defined exception in Python?

To create a User-defined Exception, we have to create a class that implements the Exception class. We can raise(throw) these exceptions using the raise keyword. These exceptions can be caught in the try-except block, just like common exceptions. In some conditions, we have multiple errors to handle in the same class.


1 Answers

TL;DR

When you see this thing, it basically means that some kind of exception has been raised in __str__() of your object. So unless the problem is trivial enough to see at the first sight (e.g. forgotten "%s"), either

  • wrap the __str__ body in a try/except clause as Anurag advices, or

  • instantiate your exception and call the __str__ (or any methods you may have overridden) manually, outside the traceback module, so that you get the full description of the exception.

Analysis

Actually this <unprintable MyException object> can come from various functions in traceback module, which, when trying to get a string (i.e. "printable") version of a value (exception), it

  1. calls str() on it, and if anything goes wrong,

  2. tries to treat it as unicode and convert it to ASCII, and if still anything goes wrong

  3. simply prints the above representation.

Responsible code (same in 2.6 and 2.7):

def _some_str(value):
    try:
        return str(value)
    except Exception:
        pass
    try:
        value = unicode(value)
        return value.encode("ascii", "backslashreplace")
    except Exception:
        pass
    return '<unprintable %s object>' % type(value).__name__

As you can see, any exceptions coming from the str() call or the unicode.encode() call are ceased in the process and only the "cryptic" representation is given.

Note on traceback module vs. Python interpreter

Contrary to what traceback documentation tells us:

It exactly mimics the behavior of the Python interpreter when it prints a stack trace.

the representation given by Python interpreter is slightly different here. As opposed to the "unprintable" message, the interpreter would simply display the name of the exception, ceasing any actual exceptions as well.

Here is a simple script that demonstrates all three approaches: leaving the exception to Python interpreter, using traceback module, or calling the function by hand.

#!/usr/bin/python

import sys, traceback

class Boom(Exception):

    def __init__(self, foo, bar, baz):
        self.foo, self.bar, self.baz = foo, bar, baz

    def __str__(self):
        return ("boom! foo: %s, bar: %s, baz: "     # ouch! forgot an %s!
                % (self.foo, self.bar, self.baz))

def goBoom(): raise Boom(foo='FOO', bar='BAR', baz='BAZ')

if __name__ == "__main__":

    if sys.argv[1].startswith("i"):
        goBoom()
        # __main__.Boom

    elif sys.argv[1].startswith("t"):
        try:    goBoom()
        except: traceback.print_exc(file=sys.stdout)
        # Boom: <unprintable Boom object>

    elif sys.argv[1].startswith("m"):
        e = Boom(foo='FOO', bar='BAR', baz='BAZ')
        e.__str__()
        # TypeError: not all arguments converted during string formatting

    else: pass
like image 195
Alois Mahdal Avatar answered Sep 27 '22 19:09

Alois Mahdal