[Python-checkins] cpython (3.4): Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.

serhiy.storchaka python-checkins at python.org
Sun Aug 17 14:34:56 CEST 2014


http://hg.python.org/cpython/rev/873002eb8087
changeset:   92134:873002eb8087
branch:      3.4
parent:      92131:7b933005c492
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Sun Aug 17 15:31:59 2014 +0300
summary:
  Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.

files:
  Lib/tkinter/__init__.py |  34 ++++++++++++++++++++++++----
  Lib/tkinter/font.py     |  19 +++++++--------
  Misc/NEWS               |   2 +
  3 files changed, 40 insertions(+), 15 deletions(-)


diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -191,6 +191,7 @@
     that constrain the type of the value returned from get()."""
     _default = ""
     _tk = None
+    _tclCommands = None
     def __init__(self, master=None, value=None, name=None):
         """Construct a variable
 
@@ -209,7 +210,7 @@
         global _varnum
         if not master:
             master = _default_root
-        self._master = master
+        self._root = master._root()
         self._tk = master.tk
         if name:
             self._name = name
@@ -222,9 +223,15 @@
             self.initialize(self._default)
     def __del__(self):
         """Unset the variable in Tcl."""
-        if (self._tk is not None and
-            self._tk.getboolean(self._tk.call("info", "exists", self._name))):
+        if self._tk is None:
+            return
+        if self._tk.getboolean(self._tk.call("info", "exists", self._name)):
             self._tk.globalunsetvar(self._name)
+        if self._tclCommands is not None:
+            for name in self._tclCommands:
+                #print '- Tkinter: deleted command', name
+                self._tk.deletecommand(name)
+            self._tclCommands = None
     def __str__(self):
         """Return the name of the variable in Tcl."""
         return self._name
@@ -244,7 +251,20 @@
 
         Return the name of the callback.
         """
-        cbname = self._master._register(callback)
+        f = CallWrapper(callback, None, self).__call__
+        cbname = repr(id(f))
+        try:
+            callback = callback.__func__
+        except AttributeError:
+            pass
+        try:
+            cbname = cbname + callback.__name__
+        except AttributeError:
+            pass
+        self._tk.createcommand(cbname, f)
+        if self._tclCommands is None:
+            self._tclCommands = []
+        self._tclCommands.append(cbname)
         self._tk.call("trace", "variable", self._name, mode, cbname)
         return cbname
     trace = trace_variable
@@ -255,7 +275,11 @@
         CBNAME is the name of the callback returned from trace_variable or trace.
         """
         self._tk.call("trace", "vdelete", self._name, mode, cbname)
-        self._master.deletecommand(cbname)
+        self._tk.deletecommand(cbname)
+        try:
+            self._tclCommands.remove(cbname)
+        except ValueError:
+            pass
     def trace_vinfo(self):
         """Return all trace callback information."""
         return [self._tk.split(x) for x in self._tk.splitlist(
diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py
--- a/Lib/tkinter/font.py
+++ b/Lib/tkinter/font.py
@@ -69,9 +69,10 @@
                  **options):
         if not root:
             root = tkinter._default_root
+        tk = getattr(root, 'tk', root)
         if font:
             # get actual settings corresponding to the given font
-            font = root.tk.splitlist(root.tk.call("font", "actual", font))
+            font = tk.splitlist(tk.call("font", "actual", font))
         else:
             font = self._set(options)
         if not name:
@@ -81,21 +82,19 @@
         if exists:
             self.delete_font = False
             # confirm font exists
-            if self.name not in root.tk.splitlist(
-                    root.tk.call("font", "names")):
+            if self.name not in tk.splitlist(tk.call("font", "names")):
                 raise tkinter._tkinter.TclError(
                     "named font %s does not already exist" % (self.name,))
             # if font config info supplied, apply it
             if font:
-                root.tk.call("font", "configure", self.name, *font)
+                tk.call("font", "configure", self.name, *font)
         else:
             # create new font (raises TclError if the font exists)
-            root.tk.call("font", "create", self.name, *font)
+            tk.call("font", "create", self.name, *font)
             self.delete_font = True
-        # backlinks!
-        self._root  = root
-        self._split = root.tk.splitlist
-        self._call  = root.tk.call
+        self._tk = tk
+        self._split = tk.splitlist
+        self._call  = tk.call
 
     def __str__(self):
         return self.name
@@ -120,7 +119,7 @@
 
     def copy(self):
         "Return a distinct copy of the current font"
-        return Font(self._root, **self.actual())
+        return Font(self._tk, **self.actual())
 
     def actual(self, option=None, displayof=None):
         "Return actual font attributes"
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,8 @@
 Library
 -------
 
+- Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.
+
 - Issue #22165: SimpleHTTPRequestHandler now supports undecodable file names.
 
 - Issue #20729: Restored the use of lazy iterkeys()/itervalues()/iteritems()

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list