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

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


http://hg.python.org/cpython/rev/0b79c702abda
changeset:   92133:0b79c702abda
branch:      2.7
parent:      92130:ffa5bfe75c3a
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Sun Aug 17 15:31:41 2014 +0300
summary:
  Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.

files:
  Lib/lib-tk/Tkinter.py |  34 ++++++++++++++++++++++++++----
  Lib/lib-tk/tkFont.py  |  19 ++++++++---------
  Misc/NEWS             |   2 +
  3 files changed, 40 insertions(+), 15 deletions(-)


diff --git a/Lib/lib-tk/Tkinter.py b/Lib/lib-tk/Tkinter.py
--- a/Lib/lib-tk/Tkinter.py
+++ b/Lib/lib-tk/Tkinter.py
@@ -201,6 +201,7 @@
     Subclasses StringVar, IntVar, DoubleVar, BooleanVar are specializations
     that constrain the type of the value returned from get()."""
     _default = ""
+    _tclCommands = None
     def __init__(self, master=None, value=None, name=None):
         """Construct a variable
 
@@ -214,7 +215,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
@@ -227,9 +228,15 @@
             self.set(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
@@ -248,7 +255,20 @@
 
         Return the name of the callback.
         """
-        cbname = self._master._register(callback)
+        f = CallWrapper(callback, None, self._root).__call__
+        cbname = repr(id(f))
+        try:
+            callback = callback.im_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
@@ -259,7 +279,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 map(self._tk.split, self._tk.splitlist(
diff --git a/Lib/lib-tk/tkFont.py b/Lib/lib-tk/tkFont.py
--- a/Lib/lib-tk/tkFont.py
+++ b/Lib/lib-tk/tkFont.py
@@ -66,9 +66,10 @@
     def __init__(self, root=None, font=None, name=None, exists=False, **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:
@@ -78,20 +79,18 @@
         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
@@ -116,7 +115,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):
         "Return actual font attributes"
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -19,6 +19,8 @@
 Library
 -------
 
+- Issue #22068: Avoided reference loops with Variables and Fonts in Tkinter.
+
 - Issue #21448: Changed FeedParser feed() to avoid O(N**2) behavior when
   parsing long line.  Original patch by Raymond Hettinger.
 

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


More information about the Python-checkins mailing list