Closed 7cddcdb8-70c4-48ae-bf34-c9cedc76ace6 closed 2 years ago
Tkinter silently discards all Tcl errors. Athough this may make Tkinter programs appear robust, it violates the fundamental principle that erros should be raised and dealt with.
In Tkinter.py is the line
self.tk.createcommand('tkerror', _tkerror)
where _tkerror is defined as
def _tkerror(err):
"""Internal function."""
pass
This disables all uses of the tcl procedure bgerror from signalling background errors to the user, including I assume any errors generated in after_idle.
Logged In: YES user_id=21627
The qualification "silently discards all Tcl errors" is definitely incorrect:
>>> Tkinter.Label(foo="bar")
Traceback (most recent call last):
File "<pyshell#1>", line 1, in ?
Tkinter.Label(foo="bar")
File "E:\Python22\lib\lib-tk\Tkinter.py", line 2261, in __init__
Widget.__init__(self, master, 'label', cnf, kw)
File "E:\Python22\lib\lib-tk\Tkinter.py", line 1756, in __init__
self.tk.call(
TclError: unknown option "-foo"
Instead, it would be more correct to say that all background errors are discarded.
Can you provide a script that demonstrates this problem? (i.e. one which behaves differently if the createcommand is removed from Tkinter.py)
Can you propose a solution for this problem? Please take into account that Tkinter behaved that way since the beginning, so any change must take backwards compatibility into account.
Logged In: YES user_id=2772
You could create a subclass of Tk that creates a different 'tkerror' command.
Anyway, CallWrapper (used when a function is passed to functions like .after_idle()) calls widget._report_exception, which calls Tk.report_callback_exception. So you can override this function too.
Neither of these things require changing Tkinter.py.
I don't seem to be permitted to attach a file, so I'm forced to paste the code.
import Tkinter as _Tkinter
class Tk(_Tkinter.Tk):
def __init__(self, *args, **kw):
_Tkinter.Tk.__init__(self, *args, **kw)
self.tk.createcommand('tkerror', self.tkerror)
def report_callback_exception(self, exc, val, tb):
print "callback exception", exc
def tkerror(self, *args):
print "tkerror", args
if __name__ == '__main__':
w = Tk()
w.after_idle(lambda: 1/0)
_Tkinter.Button(w, command="expr 1/0").pack()
w.mainloop()
Logged In: YES user_id=33229
But why is Tkinter silencing all Tk (Tcl or Python) errors in the first place?
I know I can subclass Tk, but _tkerror is clearly billed as an internal function, and the only purpose of it is to disable Tcl's bgerror reporting. This lets serious bugs go undetected.
Logged In: YES user_id=21627
It is not the case that Tkinter silently discards all Python errors. In fact, I believe it never discards any Python error silently. Can you provide an example that demonstrates otherwise?
Logged In: YES user_id=33229
That's right: Tkinter silently discards all background Tcl errors.
All errors from Tcl are silenced by def _tkerror(err): pass So Tkinter users will never see any errors that come from any bugs in Tcl, Tk or its extensions, including of course errors that may only show up under Python.
The proposed solution for this problem is simply to remove the createcomand / def _tkerror. I see no possible reason for it to be in there, and it violates the fundamental principle that errors should be raised and dealt with.
Although Tkinter behaved that way since the beginning, the change takes backwards compatibility into account because it serves no good purpose that I know of to begin with. At worst buried bugs will become visible and can be dealt with.
Logged In: YES user_id=21627
idiscovery, I still would like to see an example that demonstrates that problem.
Logged In: YES user_id=33229
What possible reason can you give to ever drop errors silently?
One example is in the Python distribution that I asked you about over a year ago: Demo/tix/tixwidgets.py
Logged In: YES user_id=21627
It's a basic engineering principle: Don't apply changes that you haven't fully understood. I still don't believe this problem could ever happen, and therefore, I still assume that Tkinter does *not*, actually, drop any errors, even though the code looks like it does.
However, my believes should be irrelevant if you can demonstrate the problem.
Logged In: YES user_id=2772
Here's an example of the type of error which is discarded by Tkinter._tkerror:
from Tkinter import Button
b = Button(command="expr 1/0")
b.pack()
b.mainloop()
When "b" is pressed, "expr" will produce an "Error: divide by zero", and the equivalent wish app will show the bgerror dialog. But replaces the normal bgerror handler with one that discards the error, doing nothing.
Of course, I've pointed out in an earlier comment how idiscovery can subclass Tk to handle this kind of error.
Logged In: YES user_id=21627
Thanks for the example. Is this the case that this can only trigger by bugs in Tcl scripts which aren't produced by Tkinter?
Logged In: YES user_id=2772
As far as I can tell, the only way _tkerror can discard an error is if it happens in an event-driven way (fired as a -command, from a binding, or from "after") and the code in question is pure tcl code.
So, for instance, in b = Button(command = lambda: 1/0) the error is reported by Tkinter.Tk.report_callback_exception. So is
Logged In: YES user_id=2772
As far as I can tell, the only way _tkerror can discard an error is if it happens in an event-driven way (fired as a -command, from a binding, or from "after") and the code in question is pure tcl code.
So, for instance, in b = Button(command = lambda: 1/0) the error is reported by Tkinter.Tk.report_callback_exception. So is b = Button(); b.configure(command=lambda: b.tk.call("expr", "1/0"))
Logged In: YES user_id=33229
It's a basic computer engineering principle not to ignore errors silently, and no one has given any reason, good or otherwise, as to why this code should be in there.
To not believe this problem could ever happen is to assume that there are never side effects in Tcl from Tkinter.
For an example of where a bug has been obscured by this, comment out the line self.tk.createcommand('tkerror', _tkerror) in Tkinter.py and run Demo/tix/tixwidgets.py Click on the ComboBox in the ExFileSelectBox demo. The popdown scrolled list widget is being created, then destroyed.
Martin I don't know how you could still assume that Tkinter does *not*, actually, drop any errors when you checked in the description of the bug over a year ago: Demo/tix/tixwidgets/BUGS.txt $Id: BUGS.txt,v 1.1 2001/03/21 07:42:07 loewis Exp $
Logged In: YES user_id=21627
The comment in BUGS.txt is too mysterious to make any sense to me. It basically says "it appears there is some problem, let me know if you understand it". This was fine as it stands, but it didn't say "this is because there is a Tcl error that is dropped by Python", and so far, you haven't indicated that this is indeed the problem.
Uncommenting the line, I get tons of "bad window path name" and "invalid command name" messages when running tixwidgets.py. This tells me that a) this might happen quite frequently in real life, and b) the user of the Python application can do nothing about it, they can't even disable the first message (it then asks whether further messages should be skipped)
I don't feel privileged to approve a change here; you should bring this up on python-dev. My feeling is that the Tcl error message is too annoying to just remove the tkerror command; printing the message to stderr might be acceptable.
Uhm, long time without discussion but I'm hoping someone interested may read this.
You have to provide a "bgerror" command to Tcl, so it will get invoked when a background error happens. Either _tkinter.c could create one for an interpreter is created or Tkinter.py could create one when a Tk instance is created. The command will be invoked with a single argument, the background error message.
If you want to play with it:
import Tkinter
def bgerr_handler(msg):
print msg, "<<"
root = Tkinter.Tk()
root.tk.createcommand("bgerror", bgerr_handler)
btn = Tkinter.Button(command="expr 1/0")
btn.pack()
root.mainloop()
To get some form of traceback you would need to play with inspect and traceback modules. But in both cases (in the example above and using inspect) an exception isn't raised at all, and if you try raising then Tcl will think the bgerror handler failed and will tell you that. For this reason I would prefer to leave to _tkinter.c to handle this.
@Guilherme I take it that you're still interested in this?
This is now 20 years old and there was no agreement on the problem. If there is a problem to fix, it will be raised again.
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields: ```python assignee = None closed_at = None created_at =
labels = ['expert-tkinter']
title = 'Tkinter sliently discards all backgrond Tcl errors'
updated_at =
user = 'https://bugs.python.org/idiscovery'
```
bugs.python.org fields:
```python
activity =
actor = 'BreamoreBoy'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Tkinter']
creation =
creator = 'idiscovery'
dependencies = []
files = []
hgrepos = []
issue_num = 639266
keywords = []
message_count = 17.0
messages = ['60278', '60279', '60280', '60281', '60282', '60283', '60284', '60285', '60286', '60287', '60288', '60289', '60290', '60291', '60292', '75162', '114188']
nosy_count = 4.0
nosy_names = ['loewis', 'jepler', 'idiscovery', 'gpolo']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue639266'
versions = []
```