Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-120220: Deprecate legacy methods for tracing variables in Tkinter
They do not work with Tcl 9.0.
Use new methods added in Python 3.6.
  • Loading branch information
serhiy-storchaka committed Jun 7, 2024
commit cd263e289a91776cc0d31c1baa17d7b5b097ad54
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ Deprecated
as a single positional argument.
(Contributed by Serhiy Storchaka in :gh:`109218`.)

* The :class:`!tkinter.Variable` methods :meth:`!trace_variable`,
:meth:`!trace_vdelete` and :meth:`!trace_vinfo` are now deprecated.
Use :meth:`!trace_add`, :meth:`!trace_remove` and :meth:`!trace_info`
instead.
(Contributed by Serhiy Storchaka in :gh:`120220`.)


Removed
=======
Expand Down
39 changes: 27 additions & 12 deletions Lib/test/test_tkinter/test_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,14 @@ def read_tracer(*args):
trace.append(('read',) + args)
def write_tracer(*args):
trace.append(('write',) + args)
cb1 = v.trace_variable('r', read_tracer)
cb2 = v.trace_variable('wu', write_tracer)
self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)])
with self.assertWarns(DeprecationWarning) as cm:
cb1 = v.trace_variable('r', read_tracer)
self.assertEqual(cm.filename, __file__)
with self.assertWarns(DeprecationWarning):
cb2 = v.trace_variable('wu', write_tracer)
with self.assertWarns(DeprecationWarning) as cm:
self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)])
self.assertEqual(cm.filename, __file__)
self.assertEqual(trace, [])

v.set('spam')
Expand All @@ -133,20 +138,30 @@ def write_tracer(*args):
self.assertEqual(trace, [('read', vname, '', 'r')])

trace = []
info = sorted(v.trace_vinfo())
v.trace_vdelete('w', cb1) # Wrong mode
self.assertEqual(sorted(v.trace_vinfo()), info)
with self.assertWarns(DeprecationWarning):
info = sorted(v.trace_vinfo())
with self.assertWarns(DeprecationWarning):
v.trace_vdelete('w', cb1) # Wrong mode
with self.assertWarns(DeprecationWarning):
self.assertEqual(sorted(v.trace_vinfo()), info)
with self.assertRaises(TclError):
v.trace_vdelete('r', 'spam') # Wrong command name
self.assertEqual(sorted(v.trace_vinfo()), info)
v.trace_vdelete('r', (cb1, 43)) # Wrong arguments
self.assertEqual(sorted(v.trace_vinfo()), info)
with self.assertWarns(DeprecationWarning):
v.trace_vdelete('r', 'spam') # Wrong command name
with self.assertWarns(DeprecationWarning):
self.assertEqual(sorted(v.trace_vinfo()), info)
with self.assertWarns(DeprecationWarning):
v.trace_vdelete('r', (cb1, 43)) # Wrong arguments
with self.assertWarns(DeprecationWarning):
self.assertEqual(sorted(v.trace_vinfo()), info)
v.get()
self.assertEqual(trace, [('read', vname, '', 'r')])

trace = []
v.trace_vdelete('r', cb1)
self.assertEqual(v.trace_vinfo(), [('wu', cb2)])
with self.assertWarns(DeprecationWarning) as cm:
v.trace_vdelete('r', cb1)
self.assertEqual(cm.filename, __file__)
with self.assertWarns(DeprecationWarning):
self.assertEqual(v.trace_vinfo(), [('wu', cb2)])
v.get()
self.assertEqual(trace, [])

Expand Down
20 changes: 16 additions & 4 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,9 +499,13 @@ def trace_variable(self, mode, callback):
Return the name of the callback.

This deprecated method wraps a deprecated Tcl method that will
likely be removed in the future. Use trace_add() instead.
be removed in Tcl 9.0. Use trace_add() instead.
"""
# TODO: Add deprecation warning
import warnings
warnings.warn(
"trace_variable() is deprecated and not supported with Tk 9, "
"use trace_add() instead",
DeprecationWarning, stacklevel=2)
cbname = self._register(callback)
self._tk.call("trace", "variable", self._name, mode, cbname)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this line be wrapped in try...except TclError... for when run with tcl/tk 9?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what to do after catching the exception?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raise a better error message -- IF the TclError message is unclear, such as not specifying that failure us 9.0 specific. I don't have 9.0 installed to test this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is "bad option "variable": must be add, info, or remove". It may be not so clear from Python's point of view, but you get a deprecation warning emitted immediately before error. I think that it is not worth to change the error. The error can also be raised for other reasons, so we would need to rely on parsing the error message, which can be changed in future.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about the case when people have DeprecationWarnings turned off. A possibility would be, when running tk9+, to immediately raise an error with more direct message 'This method does not exist in tk9, use...' right in trace_xyz itself.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you suggest to write something like this?

        try:
            self._tk.call("trace", "variable", self._name, mode, cbname)
        except _tkinter.TclError as err:
            if str(err) == 'bad option "variable": must be add, info, or remove':
                raise TypeError('trace_variable() is not supported '
                                'with Tcl 9; use trace_add() instead')
            raise

This is too verbose and fragile. If they change the error message it will not work.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this could use an exception note, that way it won't change the type. Just needs the message to be clear another error may have occurred.

return cbname
Expand All @@ -517,7 +521,11 @@ def trace_vdelete(self, mode, cbname):
This deprecated method wraps a deprecated Tcl method that will
likely be removed in the future. Use trace_remove() instead.
"""
# TODO: Add deprecation warning
import warnings
warnings.warn(
"trace_vdelete() is deprecated and not supported with Tk 9, "
"use trace_remove() instead",
DeprecationWarning, stacklevel=2)
self._tk.call("trace", "vdelete", self._name, mode, cbname)
cbname = self._tk.splitlist(cbname)[0]
for m, ca in self.trace_info():
Expand All @@ -536,7 +544,11 @@ def trace_vinfo(self):
This deprecated method wraps a deprecated Tcl method that will
likely be removed in the future. Use trace_info() instead.
"""
# TODO: Add deprecation warning
import warnings
warnings.warn(
"trace_vinfo() is deprecated and not supported with Tk 9, "
"use trace_info() instead",
DeprecationWarning, stacklevel=2)
return [self._tk.splitlist(x) for x in self._tk.splitlist(
self._tk.call("trace", "vinfo", self._name))]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Deprecate the :class:`!tkinter.Variable` methods :meth:`!trace_variable`,
:meth:`!trace_vdelete` and :meth:`!trace_vinfo`. Methods :meth:`!trace_add`,
:meth:`!trace_remove` and :meth:`!trace_info` can be used instead.