BaseException.message deprecated in Python 2.6
Asked Answered
P

8

185

I get a warning that BaseException.message is deprecated in Python 2.6 when I use the following user-defined exception:

class MyException(Exception):

    def __init__(self, message):
        self.message = message

    def __str__(self):
        return repr(self.message)

This is the warning:

DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
self.message = message

What's wrong with this? What do I have to change to get rid of the deprecation warning?

Protestantism answered 13/8, 2009 at 13:59 Comment(1)
See PEP 352 for the reasons: python.org/dev/peps/pep-0352/#retracted-ideasJiggle
Z
160

Solution - almost no coding needed

Just inherit your exception class from Exception and pass the message as the first parameter to the constructor

Example:

class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    print my # outputs 'my detailed description'

You can use str(my) or (less elegant) my.args[0] to access the custom message.

Background

In the newer versions of Python (from 2.6) we are supposed to inherit our custom exception classes from Exception which (starting from Python 2.5) inherits from BaseException. The background is described in detail in PEP 352.

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.
    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor."""

__str__ and __repr__ are already implemented in a meaningful way, especially for the case of only one arg (that can be used as message).

You do not need to repeat __str__ or __init__ implementation or create _get_message as suggested by others.

Zachar answered 17/5, 2011 at 10:58 Comment(7)
Changed BaseException to Exception as suggested by @MattZachar
Using str breaks if the exception was constructed with a unicode argument: str(MyException(u'\xe5')) raises UnicodeEncodeError. Using unicode instead of str isn't foolproof either because unicode(MyException('\xe5')) raises UnicodeDecodeError. Does this mean that if I don't know in advance if the argument is str or unicode, I have to use .args[0] where I previously used .message?Maramarabel
@Maramarabel Like virtually all Python unicode issues, this can be solved with a unicode sandwich.Gressorial
@RyanP That assumes I actually have control over what goes in. Here is a fact of life that I faced. I have to handle exceptions from multiple third party libraries. Some of those pass unicode to their exceptions and some pass str. One of the libraries even has its own class which inherits from unicode but has its own repr method, which returns unicode rather than str as required by the spec.Maramarabel
I did btw. replace all usage of .message in our project with .args[0], and that has not caused any problems for us.Maramarabel
@Maramarabel Same here.. with the deprecation of BaseException.message, is there now an untold rule to not use unicode in error strings anymore?Airscrew
@ErikvanZijst Yep that's exactly right. But only for python 2.Skippie
H
25

Yes, it's deprecated in Python 2.6 because it's going away in Python 3.0

BaseException class does not provide a way to store error message anymore. You'll have to implement it yourself. You can do this with a subclass that uses a property for storing the message.

class MyException(Exception):
    def _get_message(self): 
        return self._message
    def _set_message(self, message): 
        self._message = message
    message = property(_get_message, _set_message)

Hope this helps

Hauler answered 13/8, 2009 at 14:6 Comment(5)
How would you initialize the message during the raise? His code showed the message being set by calling MyException("some message")Homer
The methods in my example are only for implementing the message property. How the property is used is upto the coder. In this case OP uses the init and str methods that he has posted in his code.Hauler
Consider using a public variable instead of a getter/setter, if it only reads another variable. You can always upgrade that later to a @property syntax when you really need encapsulation.Romilda
@vdboor: he's using @property to disable the deprecation warning.Lollapalooza
It is unpythonic and useless to create properties that do nothing except getting and setting the value, unchecked and unchanged. The whole point of properties is to allow one to use public attributes freely, until such time as a getter or a setter is actually needed. Then you turn the public attribute into a property, without breaking any client code.Virulent
N
10

How to replicate the warning

Let me clarify the problem, as one cannot replicate this with the question's sample code, this will replicate the warning in Python 2.6 and 2.7, if you have warnings turned on (via the -W flag, the PYTHONWARNINGS environment variable, or the warnings module):

>>> error = Exception('foobarbaz')
>>> error.message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foobarbaz'

Stop using .message

I prefer repr(error), which returns a string that contains the name of the error type, the repr of the message, if there is one, and the repr of the remaining arguments.

>>> repr(error)
"Exception('foobarbaz',)"

Eliminating the warning while still using .message

And the way you get rid of the DeprecationWarning is to subclass a builtin exception as the Python designers intended:

class MyException(Exception):

    def __init__(self, message, *args):
        self.message = message
        # delegate the rest of initialization to parent
        super(MyException, self).__init__(message, *args)

>>> myexception = MyException('my message')
>>> myexception.message
'my message'
>>> str(myexception)
'my message'
>>> repr(myexception)
"MyException('my message',)"

getting just the .message attribute without error.message

If you know there was one argument, a message, to the Exception and that's what you want, it is preferable to avoid the message attribute and just take the str of the error. Say for a subclassed Exception:

class MyException(Exception):
    '''demo straight subclass'''

And usage:

>>> myexception = MyException('my message')
>>> str(myexception)
'my message'

See also this answer:

Proper way to declare custom exceptions in modern Python?

Nidify answered 29/1, 2015 at 23:6 Comment(0)
O
9
class MyException(Exception):

    def __str__(self):
        return repr(self.args[0])

e = MyException('asdf')
print e

This is your class in Python2.6 style. The new exception takes an arbitrary number of arguments.

Oneeyed answered 13/8, 2009 at 14:9 Comment(1)
The old Exception class also takes any number of arguments. You can entirely avoid the message property like what you're doing, but if that would break your existing code, you can solve the problem by implementing your own message property.Hauler
S
4

As far as I can tell, simply using a different name for the message attribute avoids the conflict with the base class, and thus stops the deprecation warning:

class MyException(Exception):

def __init__(self, message):
    self.msg = message

def __str__(self):
    return repr(self.msg)

Seems like a hack to me.

Maybe someone can explain why the warning is issued even when the subclass defines a message attribute explicitly. If the base class no longer has this attribute, there shouldn't be a problem.

Shamble answered 13/10, 2010 at 23:0 Comment(0)
H
4

Continuing on from geekQ's answer, the preferred code replacement depends on what you need to do:

### Problem
class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    ### Solution 1, fails in Python 2.x if MyException contains 🔥
    # with UnicodeEncodeError: 'ascii' codec can't encode characters in position 24-25: ordinal not in range(128)
    print(my)  # outputs 'my detailed description'

### Solution 2
# Works in Python 2.x if exception only has ASCII characters,
# should always work in Python 3.x
str(my)

### Solution 3
# Required in Python 2.x if you need to handle non-ASCII characters,
# such as δσφφδσ (as pointed out by jjc) or emoji 🔥 💕 🎁 💯 🌹
# but does not work in Python 3.x
unicode(my)

Sometimes exceptions have more than one argument, so my.args[0] is not guaranteed to provide all the relevant information.

For instance:

# Python 2.7
try:
    u'\u12345'.encode('utf-8').encode('utf-8')
except UnicodeDecodeError as e:
    print e.args[0]
    print e.args
    print str(e)

Prints as output:

ascii
('ascii', '\xe1\x88\xb45', 0, 1, 'ordinal not in range(128)')
'ascii' codec can't decode byte 0xe1 in position 0: ordinal not in range(128)

However it's a context sensitive trade off, because for instance:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
Himalayas answered 17/6, 2015 at 2:32 Comment(1)
Yes, don't overthink it. Use str(my) instead of my.message.Hartmunn
R
1

The advice to use str(myexception) leads to unicode problems in python 2.7, e.g.:

str(Exception(u'δσφφδσ'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

:(

unicode(Exception(u'δσφφδσ')) 

works as expected, and is preferred in cases where some of the content of the error string includes user input

Roadability answered 7/4, 2015 at 23:14 Comment(0)
P
1

pzrq's post says to use:

str(e)

This was exactly what I needed.

(If you are in a unicode environment, it appears that:

unicode(e)

will work, and it appears to work fine in a non-unicode environment)

Pzrq said a lot of other good stuff, but I almost missed their answer due to all the good stuff. Since I don't have 50 points I cannot comment on their answer to attempt to draw attention to the simple solution that works, and since I don't have 15 I cannot vote that answer up, but I can post (feels backward, but oh well) - so here I am posting - probably lose points for that...

Since my point is to draw attention to pzrq's answer, please don't glaze over and miss it in all the below. the first few lines of this post are the most important.

My story:

The problem I came here for was if you want to catch an exception from a class that you have no control over - what then??? I'm certainly not going to subclass all possible classes my code uses in an attempt to be able to get a message out of all possible exceptions!

I was using:

except Exception as e:
   print '%s (%s)' % (e.message,type(e))

which, as we all now know, gives the warning OP asked about (which brought me here), and this, which pzrq gives as a way to do it:

except Exception as e:
   print '%s (%s)' % (str(e),type(e))

did not.

I'm not in a unicode environment, but jjc's answer made me wonder, so I had to try it. In this context this becomes:

except Exception as e:
   print '%s (%s)' % (unicode(e),type(e))

which, to my surprise, worked exactly like str(e) - so now that's what I'm using.

Don't know if 'str(e)/unicode(e)' is the 'approved Python way', and I'll probably find out why that's not good when I get to 3.0, but one hopes that the ability to handle an unexpected exception (*) without dying and still get some information from it won't ever go away...

(*) Hmm. "unexpected exception" - I think I just stuttered!

Puryear answered 7/12, 2015 at 15:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.