Can a conversion to string raise an error?

Question:

I was wondering if converting to string i.e. str(sth) can raise an exception like for example float(sth) does?
I am asking this to know if it is necessary to wrap my code in:

try:
    x = str(value)
except ValueError:
    x = None

to be sure that execution does not stop because of conversion failure.

Also does this differ in Python 2 and 3, since the str class is different in them??

Asked By: S.Mohsen sh

||

Answers:

In theory yes, it can, but in practice it almost certainly won’t. The way that the str function works is it calls the __str__ function of the object that it’s converting to a string. Built in types like List, numeric types, and others will return values about what you would expect, [x,y,z] for a List, the number as a string for a numerical type, etc. object has a __str__ method that gives an output like <generator object <genexpr> at 0x7fdb2fa6a640>, which often isn’t very useful but won’t raise an exception.

So, calling str on any builtin is almost certainly not going to raise an exception (Admittedly, I can’t find a guarantee for this in the Python documentation, but I can’t imagine a situation where it would happen)

That said, it is possible to override the __str__ method for a custom class. If this is done, then that method could raise an exception just like any other function that a programmer could write could – an intentional exception, an uncaught IndexError or the like, etc. Even if you’re not overriding __str__, if you’re using a module that does, it’s entirely possible the author of that module made some mistake on some rare edge that will raise an exception.

The bottom line: It shouldn’t if everyone’s done their job right, but it could. In almost any practical situation I wouldn’t bother try/catching.

Answered By: James

If you encounter a custom class that explicitly raises an exception in __str__ (or __repr__ if __str__ is not defined). Or, for example a class that returns a bytes object from __str__:

class Foo:
    def __str__(self):
        return b''

str(Foo()) # TypeError: __str__ returned non-string (type bytes)

But personally, I have never seen this and I’m pretty sure no one has; it would be daft to do it. Likewise, a silly mistake in the implementation of __str__ or edge cases might create another Exception. It is interesting to see how you could push Python to these edge cases (look at @user2357112 answer here).

Other than that case, no built-ins generally raise an exception in this case since it is defined for all of them in Py2 and Py3.

For user defined classes str will use object.__str__ by default if not defined in Python 3 and, in Python 2, use it if a class is a new style class (inherits from object).

If a class is an old style class I believe it is classobj.__str__ that is used for classes and instance.__str__ for instances.

In general, I would not catch this, special cases aren’t special enough for this.

Just to add to the other answers, str() does not throw an exception in many (perhaps surprising) contexts, for example you can apply it to a function, a generator, or itself. However, I can’t imagine a situation where you would actually want to do this in practice.

>>> def a(): pass
... 
>>> str(a)
'<function a at 0x7fc43614e048>'
>>> 
>>> str(x for x in range(3))
'<generator object <genexpr> at 0x7fc434b95f10>'
>>> 
>>> str(str())
''
>>> 
Answered By: Chris_Rands

Easily. You could have a container class with a recursion-unsafe __str__ or __repr__ that ends up in a reference loop:

import numpy

x = numpy.empty([1], dtype=object)
x[0] = x
str(x)  # RuntimeError: maximum recursion depth exceeded

Or just a really highly nested object:

x = []
for i in xrange(2000):
    x = [x]

str(x)  # RuntimeError: maximum recursion depth exceeded while getting the repr of a list

Heck, even with just built-in types, you could get something a lot worse than an exception:

x = {}
for i in range(1000000):
    x = {1: x}
str(x)  # python.exe has stopped working
        # Windows can check online for a solution to the problem.

That’s a C stack overflow, since dict_repr doesn’t (currently) use Py_EnterRecursiveCall. You can’t catch that with an except block!

Back to the more mundane errors, you could have a weakref.proxy to a dead object:

import weakref

class Foo(object):
    pass

str(weakref.proxy(Foo()))  # ReferenceError: weakly-referenced object no longer exists

Or you could just write a class that raises whatever you want in __str__ or __repr__:

class Foo(object):
    def __str__(self):
        raise Exception

str(Foo())  # Raises an Exception

Specific to Python 2, you could get a case where printing an object produces partial output and then raises an exception, even though it ostensibly should have never reached the stage of printing anything:

class Foo(object):
    def __repr__(self):
        raise Exception

print [Foo()]  # Prints a single opening bracket, then raises an Exception!

This is due to the obsolete tp_print C-level hook.

You could also get a Python 2-specific UnicodeEncodeError when str-ing a unicode object that contains non-ASCII characters, since Python 2 str objects are bytestrings:

str(u'u1000')  # Fails on Python 2 only
Answered By: user2357112
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.