BaseException.message descontinuado no Python 2.6

177

Recebo um aviso de que BaseException.message está obsoleto no Python 2.6 quando uso a seguinte exceção definida pelo usuário:

class MyException(Exception):

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

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

Este é o aviso:

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

O que há de errado nisso? O que preciso alterar para me livrar do aviso de obsolescência?

desolado
fonte
9
Veja PEP 352 Pelas razões: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Respostas:

154

Solução - quase sem necessidade de codificação

Apenas herde sua classe de exceção Exceptione passe a mensagem como o primeiro parâmetro para o construtor

Exemplo:

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

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

Você pode usar str(my)ou (menos elegante) my.args[0]para acessar a mensagem personalizada.

fundo

Nas versões mais recentes do Python (da 2.6), devemos herdar nossas classes de exceção personalizadas da Exception, que (a partir do Python 2.5 ) herda da BaseException. O plano de fundo é descrito em detalhes no 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__e __repr__já foram implementados de maneira significativa, especialmente no caso de apenas um argumento (que pode ser usado como mensagem).

Você não precisa repetir __str__ou __init__implementar ou criar _get_messageconforme sugerido por outras pessoas.

geekQ
fonte
5
Alterou BaseException para Exception como sugerido por @Matt
geekQ
8
Usando strquebras se a exceção foi construída com um argumento unicode: str(MyException(u'\xe5'))raises UnicodeEncodeError. Usar em unicodevez de strnão é infalível porque unicode(MyException('\xe5'))gera UnicodeDecodeError. Isso significa que, se eu não souber antecipadamente se o argumento é strou unicode, tenho que usar .args[0]onde usei anteriormente .message?
kasperd
1
@kasperd Como praticamente todos os problemas unicode em Python, isso pode ser resolvido com um sanduíche unicode .
Ryan P
3
@RyanP Isso assume que eu realmente tenho controle sobre o que acontece. Aqui está um fato da vida que eu enfrentei. Eu tenho que lidar com exceções de várias bibliotecas de terceiros. Alguns desses passam unicode para suas exceções e outros passam str. Uma das bibliotecas ainda tem sua própria classe que herda de unicode, mas tem seu próprio método repr, que retorna unicode em vez de str, conforme exigido pela especificação.
kasperd
1
Eu fiz btw. substitua todo o uso de .messageem nosso projeto por .args[0]e isso não causou nenhum problema para nós.
kasperd
26

Sim, está obsoleto no Python 2.6 porque está desaparecendo no Python 3.0

A classe BaseException não fornece mais uma maneira de armazenar mensagens de erro. Você terá que implementá-lo você mesmo. Você pode fazer isso com uma subclasse que usa uma propriedade para armazenar a mensagem.

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

Espero que isto ajude

Sahas
fonte
1
Como você inicializaria a mensagem durante o aumento? O código dele mostrava a mensagem sendo configurada chamando MyException ("some message")
eric.frederich
Os métodos no meu exemplo são apenas para implementar a propriedade de mensagem. A forma como a propriedade é usada depende do codificador. Nesse caso, o OP usa os métodos init e str que ele postou em seu código.
Sahas
1
Considere usar uma variável pública em vez de um getter / setter, se ele apenas ler outra variável. Você sempre pode atualizar isso posteriormente para uma @propertysintaxe quando realmente precisar de encapsulamento.
vdboor
2
@vdboor: ele está usando @propertypara desativar o aviso de reprovação .
bukzor
É antitônico e inútil criar propriedades que não fazem nada, exceto obter e definir o valor, desmarcado e inalterado. O objetivo das propriedades é permitir que se use atributos públicos livremente, até que um getter ou setter seja realmente necessário. Em seguida, você transforma o atributo público em uma propriedade, sem quebrar nenhum código do cliente.
Luciano Ramalho
9
class MyException(Exception):

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

e = MyException('asdf')
print e

Esta é sua classe no estilo Python2.6. A nova exceção leva um número arbitrário de argumentos.

Maxim Sloyko
fonte
1
A antiga classe Exception também aceita qualquer número de argumentos. Você pode evitar completamente a propriedade de mensagem como o que está fazendo, mas se isso interromper o código existente, poderá resolver o problema implementando sua própria propriedade de mensagem.
Sahas
8

Como replicar o aviso

Deixe-me esclarecer o problema, como não é possível replicar isso com o código de exemplo da pergunta, ele replicará o aviso no Python 2.6 e 2.7, se você tiver avisos ativados (por meio do -Wsinalizador , da PYTHONWARNINGSvariável de ambiente ou do módulo de avisos ):

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

Pare de usar .message

Eu prefiro repr(error), que retorna uma string que contém o nome do tipo de erro, a repr da mensagem, se houver, e a repr dos argumentos restantes.

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

Eliminando o aviso enquanto ainda estiver usando .message

E a maneira como você se livrar do DeprecationWarningé a subclasse uma exceção incorporado como os designers Python destina:

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

ficando apenas o .messageatributo semerror.message

Se você sabe que houve um argumento, uma mensagem, para a exceção e é isso que você deseja, é preferível evitar o atributo de mensagem e apenas aceitar stro erro. Diga para uma subclasse Exception:

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

E uso:

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

Veja também esta resposta:

Maneira correta de declarar exceções personalizadas no Python moderno?

Aaron Hall
fonte
4

Tanto quanto posso dizer, o simples uso de um nome diferente para o atributo de mensagem evita o conflito com a classe base e, portanto, interrompe o aviso de descontinuação:

class MyException(Exception):

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

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

Parece um truque para mim.

Talvez alguém possa explicar por que o aviso é emitido, mesmo quando a subclasse define explicitamente um atributo da mensagem. Se a classe base não tiver mais esse atributo, não deverá haver um problema.

Hollister
fonte
4

Continuando com a resposta do geekQ , a substituição preferida do código depende do que você precisa fazer:

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

Às vezes, as exceções têm mais de um argumento, então my.args[0] não há garantia de fornecer todas as informações relevantes.

Por exemplo:

# 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 como saída:

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)

No entanto, é uma troca sensível ao contexto, porque, por exemplo:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
pzrq
fonte
1
Sim, não pense demais. Use em str(my)vez de my.message.
Christian Long
1

O conselho para usar str (myexception) leva a problemas unicode no python 2.7, por exemplo:

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

funciona como esperado e é preferido nos casos em que parte do conteúdo da sequência de erros inclui entrada do usuário

jjc
fonte
1

A publicação de pzrq diz para usar:

str(e)

Era exatamente disso que eu precisava.

(Se você estiver em um ambiente unicode, parece que:

unicode(e)

funcionará e parece funcionar bem em um ambiente não unicode)

Pzrq disse muitas outras coisas boas, mas eu quase perdi a resposta devido a todas as coisas boas. Como não tenho 50 pontos, não posso comentar a resposta deles para tentar chamar a atenção para a solução simples que funciona, e como não tenho 15, não posso votar nessa resposta, mas posso postar (parece atrasado, mas oh bem) - então aqui estou postando - provavelmente perdem pontos por isso ...

Como o meu objetivo é chamar a atenção para a resposta do pzrq, por favor, não encha o olho e perca tudo isso abaixo. as primeiras linhas deste post são as mais importantes.

Minha história:

O problema para o qual vim aqui foi se você deseja capturar uma exceção de uma classe que não tem controle - e então ??? Certamente não vou subclassificar todas as classes possíveis que meu código usa para tentar obter uma mensagem de todas as possíveis exceções!

Eu estava usando:

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

que, como todos sabemos agora, dá o aviso que o OP perguntou sobre (o que me trouxe aqui), e este, que o pzrq fornece como uma maneira de fazê-lo:

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

nao fiz.

Não estou em um ambiente unicode, mas a resposta de jjc me fez pensar, então tive que tentar. Nesse contexto, isso se torna:

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

que, para minha surpresa, funcionou exatamente como str (e) - então agora é isso que estou usando.

Não sei se 'str (e) / unicode (e)' é a 'maneira Python aprovada', e provavelmente vou descobrir por que isso não é bom quando chego ao 3.0, mas esperamos que a capacidade de lidar com um exceção inesperada (*) sem morrer e ainda assim obter algumas informações, nunca desaparecerá ...

(*) Hmm. "exceção inesperada" - acho que gaguejei!

enferrujado
fonte