Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions 7 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 39 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 30 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
Morty Proxy This is a proxified and sanitized view of the page, visit original site.