BaseException.message obsolète dans Python 2.6

177

Je reçois un avertissement indiquant que BaseException.message est obsolète dans Python 2.6 lorsque j'utilise l'exception définie par l'utilisateur suivante:

class MyException(Exception):

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

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

Voici l'avertissement:

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

Quel est le problème avec ça? Que dois-je changer pour me débarrasser de l'avertissement d'obsolescence?

désolé
la source
9
Voir PEP 352 pour les raisons: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Réponses:

154

Solution - presque aucun codage nécessaire

Héritez simplement votre classe d'exception Exceptionet passez le message comme premier paramètre au constructeur

Exemple:

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

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

Vous pouvez utiliser str(my)ou (moins élégant) my.args[0]pour accéder au message personnalisé.

Contexte

Dans les versions plus récentes de Python (à partir de 2.6), nous sommes censés hériter de nos classes d'exceptions personnalisées d'Exception qui (à partir de Python 2.5 ) héritent de BaseException. Le contexte est décrit en détail dans la 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__et __repr__sont déjà implémentés de manière significative, en particulier pour le cas d'un seul argument (qui peut être utilisé comme message).

Vous n'avez pas besoin de répéter __str__ou de __init__mettre en œuvre ou de créer _get_messagecomme suggéré par d'autres.

geekQ
la source
5
Changement de BaseException en Exception comme suggéré par @Matt
geekQ
8
Utilisation de strbreaks si l'exception a été construite avec un argument unicode: str(MyException(u'\xe5'))raises UnicodeEncodeError. Utiliser à la unicodeplace de strn'est pas infaillible non plus car unicode(MyException('\xe5'))lève UnicodeDecodeError. Cela signifie-t-il que si je ne sais pas à l'avance si l'argument est strou unicode, je dois utiliser .args[0]là où j'ai précédemment utilisé .message?
kasperd
1
@kasperd Comme pratiquement tous les problèmes Unicode Python, cela peut être résolu avec un sandwich Unicode .
Ryan P
3
@RyanP Cela suppose que j'ai réellement le contrôle sur ce qui se passe. Voici un fait de la vie auquel j'ai été confronté. Je dois gérer les exceptions de plusieurs bibliothèques tierces. Certains d'entre eux passent unicode à leurs exceptions et certains passent str. L'une des bibliothèques a même sa propre classe qui hérite de unicode mais a sa propre méthode repr, qui renvoie unicode plutôt que str comme l'exige la spécification.
kasperd
1
Je l'ai fait btw. remplacer toute utilisation de .messagedans notre projet par .args[0], et cela ne nous a posé aucun problème.
kasperd
26

Oui, il est obsolète dans Python 2.6 car il disparaît dans Python 3.0

La classe BaseException ne fournit plus un moyen de stocker le message d'erreur. Vous devrez le mettre en œuvre vous-même. Vous pouvez le faire avec une sous-classe qui utilise une propriété pour stocker le 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)

J'espère que cela t'aides

Sahas
la source
1
Comment initialiseriez-vous le message pendant la relance? Son code indiquait que le message était défini en appelant MyException ("some message")
eric.frederich
Les méthodes de mon exemple ne servent qu'à implémenter la propriété message. La façon dont la propriété est utilisée dépend du codeur. Dans ce cas, OP utilise les méthodes init et str qu'il a postées dans son code.
Sahas
1
Pensez à utiliser une variable publique au lieu d'un getter / setter, s'il ne lit qu'une autre variable. Vous pouvez toujours mettre à niveau cela plus tard vers une @propertysyntaxe lorsque vous avez vraiment besoin d'encapsulation.
vdboor
2
@vdboor: il utilise @propertypour désactiver l'avertissement d' obsolescence .
bukzor
Il est impythonique et inutile de créer des propriétés qui ne font rien sauf obtenir et définir la valeur, non cochée et inchangée. L'intérêt des propriétés est de permettre à chacun d'utiliser librement les attributs publics, jusqu'à ce qu'un getter ou un setter soit réellement nécessaire. Ensuite, vous transformez l'attribut public en propriété, sans casser le code client.
Luciano Ramalho
9
class MyException(Exception):

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

e = MyException('asdf')
print e

Ceci est votre classe dans le style Python2.6. La nouvelle exception prend un nombre arbitraire d'arguments.

Maxim Sloyko
la source
1
L'ancienne classe Exception prend également n'importe quel nombre d'arguments. Vous pouvez entièrement éviter la propriété de message comme ce que vous faites, mais si cela briserait votre code existant, vous pouvez résoudre le problème en implémentant votre propre propriété de message.
Sahas
8

Comment reproduire l'avertissement

Permettez-moi de clarifier le problème, car on ne peut pas le répliquer avec l'exemple de code de la question, cela répliquera l'avertissement dans Python 2.6 et 2.7, si vous avez des avertissements activés (via l' -Windicateur , la PYTHONWARNINGSvariable d'environnement ou le module d'avertissement ):

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

Arrête d'utiliser .message

Je préfère repr(error), qui retourne une chaîne qui contient le nom du type d'erreur, la repr du message, s'il y en a une, et la repr des arguments restants.

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

Élimination de l'avertissement pendant l'utilisation .message

Et la façon dont vous vous débarrasser de l' DeprecationWarningest de sous - classe une exception builtin que les concepteurs Python destinés:

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',)"

obtenir juste l' .messageattribut sanserror.message

Si vous savez qu'il y avait un argument, un message, à l'exception et que c'est ce que vous voulez, il est préférable d'éviter l'attribut message et de prendre simplement le strde l'erreur. Dites pour un sous-classé Exception:

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

Et l'utilisation:

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

Voir aussi cette réponse:

Comment déclarer des exceptions personnalisées dans Python moderne?

Salle Aaron
la source
4

Pour autant que je sache, utiliser simplement un nom différent pour l'attribut message évite le conflit avec la classe de base et arrête ainsi l'avertissement de dépréciation:

class MyException(Exception):

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

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

Cela me semble être un hack.

Peut-être que quelqu'un peut expliquer pourquoi l'avertissement est émis même lorsque la sous-classe définit explicitement un attribut de message. Si la classe de base n'a plus cet attribut, il ne devrait pas y avoir de problème.

Hollister
la source
4

Dans la continuité de la réponse de geekQ , le remplacement de code préféré dépend de ce que vous devez faire:

### 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)

Parfois, les exceptions ont plus d'un argument, il my.args[0]n'est donc pas garanti de fournir toutes les informations pertinentes.

Par exemple:

# 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)

Imprime comme sortie:

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)

Cependant, c'est un compromis sensible au contexte, car par exemple:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
pzrq
la source
1
Oui, n'y réfléchissez pas trop. Utilisez à la str(my)place de my.message.
Christian Long
1

Le conseil d'utiliser str (myexception) conduit à des problèmes Unicode en python 2.7, par exemple:

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'δσφφδσ')) 

fonctionne comme prévu et est préférable dans les cas où une partie du contenu de la chaîne d'erreur inclut une entrée utilisateur

jjc
la source
1

Le message de pzrq dit d'utiliser:

str(e)

C'était exactement ce dont j'avais besoin.

(Si vous êtes dans un environnement Unicode, il semble que:

unicode(e)

fonctionnera, et cela semble fonctionner correctement dans un environnement non-unicode)

Pzrq a dit beaucoup d'autres bonnes choses, mais j'ai failli manquer leur réponse à cause de toutes les bonnes choses. Comme je n'ai pas 50 points, je ne peux pas commenter leur réponse pour tenter d'attirer l'attention sur la solution simple qui fonctionne, et comme je n'en ai pas 15, je ne peux pas voter pour cette réponse, mais je peux poster (je me sens en arrière, mais eh bien) - alors ici je poste - probablement perdre des points pour ça ...

Étant donné que mon objectif est d'attirer l'attention sur la réponse de pzrq, veuillez ne pas le glacer et le manquer dans tout ce qui suit. les premières lignes de cet article sont les plus importantes.

Mon histoire:

Le problème pour lequel je suis venu ici était si vous vouliez attraper une exception d'une classe sur laquelle vous n'avez aucun contrôle - que faire alors ??? Je ne vais certainement pas sous-classer toutes les classes possibles que mon code utilise pour tenter d'obtenir un message de toutes les exceptions possibles!

J'utilisais:

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

qui, comme nous le savons tous maintenant, donne l'avertissement OP demandé (qui m'a amené ici), et ce, que pzrq donne comme moyen de le faire:

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

pas.

Je ne suis pas dans un environnement unicode, mais la réponse de jjc m'a fait me demander, alors j'ai dû l'essayer. Dans ce contexte, cela devient:

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

qui, à ma grande surprise, fonctionnait exactement comme str (e) - c'est donc ce que j'utilise.

Je ne sais pas si 'str (e) / unicode (e)' est la 'méthode Python approuvée', et je découvrirai probablement pourquoi ce n'est pas bon quand j'arrive à 3.0, mais on espère que la capacité de gérer un exception inattendue (*) sans mourir et obtenir encore des informations ne disparaîtra jamais ...

(*) Hmm. "exception inattendue" - je pense que je viens de bégayer!

voiture rouillée
la source