Skip to content

gh-120220: Deprecate legacy methods for tracing variables in Tkinter #120223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,13 @@ Deprecated
Deprecate :meth:`symtable.Class.get_methods` due to the lack of interest.
(Contributed by Bénédikt Tran in :gh:`119698`.)

* :mod:`tkinter`:
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`.)

* :mod:`urllib.parse`:
Accepting objects with false values (like ``0`` and ``[]``) except empty
strings, byte-like objects and ``None`` in :mod:`urllib.parse` functions
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 @@ -122,9 +122,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 @@ -135,20 +140,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
30 changes: 21 additions & 9 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,14 @@ 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.
This deprecated method wraps a deprecated Tcl method 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 Tcl 9; "
"use trace_add() instead.",
DeprecationWarning, stacklevel=2)
cbname = self._register(callback)
self._tk.call("trace", "variable", self._name, mode, cbname)
Copy link
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
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
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
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
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
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.

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 @@ -516,10 +520,14 @@ def trace_vdelete(self, mode, cbname):
MODE is one of "r", "w", "u" for read, write, undefine.
CBNAME is the name of the callback returned from trace_variable or trace.

This deprecated method wraps a deprecated Tcl method that will
likely be removed in the future. Use trace_remove() instead.
This deprecated method wraps a deprecated Tcl method removed
in Tcl 9.0. Use trace_remove() instead.
"""
# TODO: Add deprecation warning
import warnings
warnings.warn(
"trace_vdelete() is deprecated and not supported with Tcl 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 @@ -535,10 +543,14 @@ def trace_vdelete(self, mode, cbname):
def trace_vinfo(self):
"""Return all trace callback information.

This deprecated method wraps a deprecated Tcl method that will
likely be removed in the future. Use trace_info() instead.
This deprecated method wraps a deprecated Tcl method removed
in Tcl 9.0. Use trace_info() instead.
"""
# TODO: Add deprecation warning
import warnings
warnings.warn(
"trace_vinfo() is deprecated and not supported with Tcl 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.
Loading