Можно ли вызвать исключение, которое включает не английские символы в Python 2?
Я пытаюсь вызвать исключение в Python 2.7.x, который включает Unicode в сообщении. Я не могу заставить это работать.
Не поддерживается или не рекомендуется включать Unicode в сообщение об ошибке? Или мне нужно смотреть на sys.stderr?
# -*- coding: utf-8 -*-
class MyException(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return self.value
def __repr__(self):
return self.value
def __unicode__(self):
return self.value
desc = u'something bad with field \u4443'
try:
raise MyException(desc)
except MyException as e:
print(u'Inside try block : ' + unicode(e))
# here is what i wish to make work
raise MyException(desc)
Запущенный скрипт выдает результат ниже. Внутри моей попытки / кроме я могу напечатать строку без проблем.
Моя проблема вне попытки / кроме.
Inside try block : something bad with field 䑃
Traceback (most recent call last):
File "C:\Python27\lib\bdb.py", line 387, in run
exec cmd in globals, locals
File "C:\Users\ghis3080\r.py", line 25, in <module>
raise MyException(desc)
MyException: something bad with field \u4443
Заранее спасибо.
3 ответа
Поведение зависит от версии Python и среды. На Python 3 обработчик ошибок кодировки символов для sys.stderr
всегда 'backslashreplace'
:
from __future__ import unicode_literals, print_function
import sys
s = 'unicode "\u2323" smile'
print(s)
print(s, file=sys.stderr)
try:
raise RuntimeError(s)
except Exception as e:
print(e.args[0])
print(e.args[0], file=sys.stderr)
raise
python3:
$ PYTHONIOENCODING=ascii:ignore python3 raise_unicode.py
unicode "" smile
unicode "\u2323" smile
unicode "" smile
unicode "\u2323" smile
Traceback (most recent call last):
File "raise_unicode.py", line 8, in <module>
raise RuntimeError(s)
RuntimeError: unicode "\u2323" smile
$ PYTHONIOENCODING=ascii:ignore python2 raise_unicode.py
unicode "" smile
unicode "" smile
unicode "" smile
unicode "" smile
Traceback (most recent call last):
File "raise_unicode.py", line 8, in <module>
raise RuntimeError(s)
RuntimeError
То есть на моей системе сообщение об ошибке съедено на python2.
Примечание: в Windows вы можете попробовать:
T:\> set PYTHONIOENCODING=ascii:ignore
T:\> python raise_unicode.py
Для сравнения:
$ python3 raise_unicode.py
unicode "⌣" smile
unicode "⌣" smile
unicode "⌣" smile
unicode "⌣" smile
Traceback (most recent call last):
File "raise_unicode.py", line 8, in <module>
raise RuntimeError(s)
RuntimeError: unicode "⌣" smile
Вот как работает Python. Я верю, что то, что вы видите, исходит от traceback._some_string()
в основной библиотеке Python. В этом модуле, когда выполняется трассировка стека, код этого метода сначала пытается преобразовать сообщение, используя str()
затем, если это вызывает исключение, преобразует сообщение, используя unicode()
затем преобразует его в ascii, используя encode("ascii", "backslashreplace")
, Вы получаете корректный вывод, и все работает правильно, я предполагаю, что Python делает все возможное, чтобы псевдо-вниз преобразовать сообщение об ошибке, чтобы оно отображалось без проблем, независимо от платформы, выполняющей его. Это просто кодовая точка Unicode для вашего персонажа. Это не происходит в вашем try/except
block, потому что это преобразование является чем-то специфическим для механизма, который производит трассировки стека (например, в случае необработанных исключений).
В моем случае ваш пример работал как надо, печатая хороший юникод.
Но иногда у вас возникает много проблем с печатью стека исключений без (или с экранированными / обратными косыми чертами) символами Юникода. Можно преодолеть препятствие и напечатать обычные сообщения.
Пример проблемы с выводом (Python 2.7, linux):
# -*- coding: utf-8 -*-
desc = u'something bad with field ¾'
raise SyntaxError(desc.encode('utf-8', 'replace'))
Будет напечатано только усеченное или прикрученное сообщение:
~/.../sources/C_patch$ python SO.py
Traceback (most recent call last):
File "SO.py", line 25, in <module>
raise SyntaxError(desc)
SyntaxError
Чтобы реально увидеть неизмененный юникод, вы можете закодировать его в необработанные байты и передать в объект исключения:
# -*- coding: utf-8 -*-
desc = u'something bad with field ¾'
raise SyntaxError(desc.encode('utf-8', 'replace'))
На этот раз вы увидите полное сообщение:
~/.../sources/C_patch$ python SO.py
Traceback (most recent call last):
File "SO.py", line 3, in <module>
raise SyntaxError(desc.encode('utf-8', 'replace'))
SyntaxError: something bad with field ¾
Ты можешь сделать value.encode('utf-8', 'replace')
в вашем конструкторе, если хотите, но с системным исключением, вам придется сделать это в raise
утверждение, как в примере.
Подсказка взята отсюда: Преодоление разочарования: правильно использовать Unicode в Python2 (есть большая библиотека со многими помощниками, и все они могут быть сокращены до приведенного выше примера).