можно ли вызвать исключение, которое включает неанглийские символы в python 2?

Я пытаюсь вызвать исключение в python 2.7.x, которое включает юникод в сообщение. Кажется, я не могу заставить это работать.

Не поддерживается или не рекомендуется включать юникод в сообщение об ошибке? Или мне нужно смотреть на 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

Заранее спасибо.


person gotchula    schedule 06.11.2012    source источник
comment
Относится к stackoverflow.com/q/29096702/4653485.   -  person Jérôme    schedule 09.01.2019


Ответы (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

питон3:

$ 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

python2:

$ 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
person jfs    schedule 06.11.2012

Вот как работает Python. Я считаю, что то, что вы видите, исходит из traceback._some_string() в основной библиотеке Python. В этом модуле, когда выполняется трассировка стека, код в этом методе сначала пытается преобразовать сообщение, используя str(), затем, если это вызывает исключение, преобразует сообщение, используя unicode(), а затем преобразует его в ascii, используя encode("ascii", "backslashreplace"). Вы получаете действительный вывод, и все работает правильно, я предполагаю, что Python делает все возможное для псевдо-понижающего преобразования сообщения об ошибке, чтобы оно отображалось без проблем независимо от платформы, на которой оно выполняется. Это просто кодовая точка юникода для вашего персонажа. Этого не происходит в вашем блоке try/except, потому что это преобразование специфично для механизма, создающего трассировку стека (например, в случае необработанных исключений).

person Silas Ray    schedule 06.11.2012

В моем случае ваш пример работал как надо, печатая красивый юникод.

Но иногда у вас возникает много проблем со стеком исключений, напечатанным без (или с экранированными / обратными косыми чертами) символов юникода. Можно преодолеть препятствие и распечатать обычные сообщения.

Пример проблемы с выводом (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, как в примере.

Подсказка взята отсюда: Преодоление разочарования: правильное использование юникода в python2 (есть большая библиотека с множеством хелперов, и все они могут быть урезаны до примера выше).

person dmitry_romanov    schedule 27.11.2014
comment
.encode('utf-8') мне очень помог, мне даже не пришлось использовать второй параметр. - person Visgean Skeloru; 09.12.2014