Это дополнительный вопрос от этого , где предлагается реализовать bgerror, чтобы фактически получить ответ на то, что пошло не так в фоновом режиме. Однако я немного запутался, поскольку я понимаю документацию, должно быть достаточно определить proc
с именем bgerror
, которое будет вызываться интерпретатором, не так ли?
Моя попытка ниже показывает, как я пытаюсь это реализовать, но безрезультатно.
import tkinter as tk
tcl = tk.Tk()
####work around for puts in python
def puts(inp):
print(inp)
cmd = tcl.register(puts)
tcl.eval(
'proc puts {args} {' +
f'{cmd} [join $args " "]' +
'}')
tcl.call('puts', 'test') #test puts
#attempt to implement bgerror
tcl.eval('''
proc bgerror {message} {
set timestamp [clock format [clock seconds]]
puts "$timestamp: bgerror in $::argv '$message'"
}''')
#setup a failing proc
tcl.eval('''
after 500 {error "this is an error"}
''')
tcl.mainloop()
Кроме того, было бы неплохо узнать, правильный ли это вообще подход, поскольку в документации для новых приложений предлагается использовать interp bgerror. Но я снова в замешательстве, потому что, похоже, нет индикатора, где я мог бы найти path
для этих вызовов, или они предназначены исключительно для дочернего переводчика?
После небольшого исследования я обнаружил, что это (давно) нерешенная проблема в python. Сравните "Выпуск 639266". Чтобы обойти это, вы должны использовать createcommand
, как показано в недавнем (2010 г.) обновленном разговоре.
Обновленный код выглядит так:
import tkinter as tk
tcl = tk.Tk()
#attempt to implement bgerror
def bgerr_handler(msg):
print (msg, "<<")
tcl.tk.createcommand("bgerror", bgerr_handler)
#setup a failing proc
tcl.eval('''
after 500 {error "this is an error"}
''')
tcl.mainloop()
Обновлять: Они даже закрыли эту проблему на github с:
Этому уже 20 лет, а согласия по проблеме не было. Если есть проблема, которую нужно исправить, она будет поднята снова.
Также обратите внимание, что в самой документации указано, что:
Если вы пишете код, который будет использоваться другими как часть пакет или другую библиотеку, избегайте bgerror. Причина этого в том, что программист приложения может также захотеть определите bgerror или используйте другой код, который делает это и, следовательно, будет иметь проблемы с интеграцией вашего кода.
Однако в случае tkinter я бы сделал исключение из этой рекомендации, так как довольно редко разработчики Python хотят реализовать свой собственный tcl bgerror
, и более вероятно, что они будут удивлены молчаливым игнорируемым исключением и столкнутся с «просто не работающим». код".
В этом случае вам лучше использовать явную регистрацию вместо использования неявной регистрации на основе именования, так как это позволит вам получить доступ ко всей информации об ошибке (включая трассировку стека!), а не только к сообщению.
import tkinter
tcl = tkinter.Tcl()
# Fortunately, we can put the complex stuff in a function
def install_bgerror_handler(tcl, handler):
def type_thunk(message, options):
# Call internal command to convert Tcl dict to Python dict
handler(message, tkinter._splitdict(tcl, options))
tcl.createcommand(handler.__name__, type_thunk)
tcl.call("interp", "bgerror", "", handler.__name__)
def bgerr_handler(message, options):
print(message, "<<", options)
install_bgerror_handler(tcl, bgerr_handler)
# demonstrate with a background failure
tcl.eval('''
after 500 {error "this is an error"}
''')
tcl.mainloop()
Сообщение, распечатываемое в этом случае:
this is an error << {'code': '1', 'level': '0', 'errorstack': 'INNER {returnImm {this is an error} {}}', 'errorcode': 'NONE', 'errorinfo': 'this is an error\n while executing\n"error "this is an error""\n ("after" script)', 'errorline': '1'}
Трассировка стека доступна в обработчике как options["errorinfo"]
. errorcode
и errorline
тоже могут быть интересны. (Преобразование на основе kwargs оставлено в качестве упражнения, но имейте в виду, что набор ключей в этом словаре не является закрытым набором; более сложные ошибки могут включать ключи, не перечисленные выше.)
Рекомендуется проявлять особую осторожность, чтобы избежать фактического создания исключений из вашего обработчика. Все сработает, но, вероятно, это плохой план.
Я предположил, что использование __name__
подходит для создания имени. Для правильного производственного кода, вероятно, потребуется какая-то генерация символов, такая как то, что tkinter делает внутри для обратных вызовов.
Я не уверен, зачем нужно генерировать уникальное имя. Это просто из-за осторожности, чтобы случайно не перезаписать обработчик, или для этого есть другая причина?
Чисто из осторожности.
Определение фонового обработчика ошибок — это хорошо и рекомендуется для приложения, но не очень хорошо для библиотеки.