[Python-checkins] cpython (merge 3.4 -> default): Issue #22226: Added private function _splitdict() in the Tkinter module.

serhiy.storchaka python-checkins at python.org
Sat Sep 6 21:52:12 CEST 2014


http://hg.python.org/cpython/rev/11cf18ec1900
changeset:   92374:11cf18ec1900
parent:      92371:1f365cea77b8
parent:      92373:f89995a4ec11
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Sat Sep 06 22:49:07 2014 +0300
summary:
  Issue #22226: Added private function _splitdict() in the Tkinter module.

First letter no longer is stripped from the "status" key in
the result of Treeview.heading().

files:
  Lib/test/test_tcl.py                        |  37 ++++-
  Lib/tkinter/__init__.py                     |  78 +++++-----
  Lib/tkinter/test/test_ttk/test_functions.py |  27 +--
  Lib/tkinter/ttk.py                          |  60 +++----
  Misc/NEWS                                   |   3 +
  5 files changed, 112 insertions(+), 93 deletions(-)


diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -7,7 +7,7 @@
 _tkinter = support.import_module('_tkinter')
 
 # Make sure tkinter._fix runs to set up the environment
-support.import_fresh_module('tkinter')
+tkinter = support.import_fresh_module('tkinter')
 
 from tkinter import Tcl
 from _tkinter import TclError
@@ -566,6 +566,41 @@
         for arg, res in testcases:
             self.assertEqual(split(arg), res, msg=arg)
 
+    def test_splitdict(self):
+        splitdict = tkinter._splitdict
+        tcl = self.interp.tk
+
+        arg = '-a {1 2 3} -something foo status {}'
+        self.assertEqual(splitdict(tcl, arg, False),
+            {'-a': '1 2 3', '-something': 'foo', 'status': ''})
+        self.assertEqual(splitdict(tcl, arg),
+            {'a': '1 2 3', 'something': 'foo', 'status': ''})
+
+        arg = ('-a', (1, 2, 3), '-something', 'foo', 'status', '{}')
+        self.assertEqual(splitdict(tcl, arg, False),
+            {'-a': (1, 2, 3), '-something': 'foo', 'status': '{}'})
+        self.assertEqual(splitdict(tcl, arg),
+            {'a': (1, 2, 3), 'something': 'foo', 'status': '{}'})
+
+        self.assertRaises(RuntimeError, splitdict, tcl, '-a b -c ')
+        self.assertRaises(RuntimeError, splitdict, tcl, ('-a', 'b', '-c'))
+
+        arg = tcl.call('list',
+                        '-a', (1, 2, 3), '-something', 'foo', 'status', ())
+        self.assertEqual(splitdict(tcl, arg),
+            {'a': (1, 2, 3) if self.wantobjects else '1 2 3',
+             'something': 'foo', 'status': ''})
+
+        if tcl_version >= (8, 5):
+            arg = tcl.call('dict', 'create',
+                           '-a', (1, 2, 3), '-something', 'foo', 'status', ())
+            if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5):
+                # Before 8.5.5 dicts were converted to lists through string
+                expected = {'a': '1 2 3', 'something': 'foo', 'status': ''}
+            else:
+                expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
+            self.assertEqual(splitdict(tcl, arg), expected)
+
 
 class BigmemTclTest(unittest.TestCase):
 
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -112,6 +112,29 @@
 try: _cnfmerge = _tkinter._cnfmerge
 except AttributeError: pass
 
+def _splitdict(tk, v, cut_minus=True, conv=None):
+    """Return a properly formatted dict built from Tcl list pairs.
+
+    If cut_minus is True, the supposed '-' prefix will be removed from
+    keys. If conv is specified, it is used to convert values.
+
+    Tcl list is expected to contain an even number of elements.
+    """
+    t = tk.splitlist(v)
+    if len(t) % 2:
+        raise RuntimeError('Tcl list representing a dict is expected '
+                           'to contain an even number of elements')
+    it = iter(t)
+    dict = {}
+    for key, value in zip(it, it):
+        key = str(key)
+        if cut_minus and key[0] == '-':
+            key = key[1:]
+        if conv:
+            value = conv(value)
+        dict[key] = value
+    return dict
+
 class Event:
     """Container for the properties of an event.
 
@@ -1393,15 +1416,10 @@
         else:
             options = self._options(cnf, kw)
         if not options:
-            res = self.tk.call('grid',
-                       command, self._w, index)
-            words = self.tk.splitlist(res)
-            dict = {}
-            for i in range(0, len(words), 2):
-                key = words[i][1:]
-                value = words[i+1]
-                dict[key] = self._gridconvvalue(value)
-            return dict
+            return _splitdict(
+                self.tk,
+                self.tk.call('grid', command, self._w, index),
+                conv=self._gridconvvalue)
         res = self.tk.call(
                   ('grid', command, self._w, index)
                   + options)
@@ -1961,16 +1979,10 @@
     def pack_info(self):
         """Return information about the packing options
         for this widget."""
-        words = self.tk.splitlist(
-            self.tk.call('pack', 'info', self._w))
-        dict = {}
-        for i in range(0, len(words), 2):
-            key = words[i][1:]
-            value = words[i+1]
-            if str(value)[:1] == '.':
-                value = self._nametowidget(value)
-            dict[key] = value
-        return dict
+        d = _splitdict(self.tk, self.tk.call('pack', 'info', self._w))
+        if 'in' in d:
+            d['in'] = self.nametowidget(d['in'])
+        return d
     info = pack_info
     propagate = pack_propagate = Misc.pack_propagate
     slaves = pack_slaves = Misc.pack_slaves
@@ -2012,16 +2024,10 @@
     def place_info(self):
         """Return information about the placing options
         for this widget."""
-        words = self.tk.splitlist(
-            self.tk.call('place', 'info', self._w))
-        dict = {}
-        for i in range(0, len(words), 2):
-            key = words[i][1:]
-            value = words[i+1]
-            if str(value)[:1] == '.':
-                value = self._nametowidget(value)
-            dict[key] = value
-        return dict
+        d = _splitdict(self.tk, self.tk.call('place', 'info', self._w))
+        if 'in' in d:
+            d['in'] = self.nametowidget(d['in'])
+        return d
     info = place_info
     slaves = place_slaves = Misc.place_slaves
 
@@ -2061,16 +2067,10 @@
     def grid_info(self):
         """Return information about the options
         for positioning this widget in a grid."""
-        words = self.tk.splitlist(
-            self.tk.call('grid', 'info', self._w))
-        dict = {}
-        for i in range(0, len(words), 2):
-            key = words[i][1:]
-            value = words[i+1]
-            if str(value)[:1] == '.':
-                value = self._nametowidget(value)
-            dict[key] = value
-        return dict
+        d = _splitdict(self.tk, self.tk.call('grid', 'info', self._w))
+        if 'in' in d:
+            d['in'] = self.nametowidget(d['in'])
+        return d
     info = grid_info
     location = grid_location = Misc.grid_location
     propagate = grid_propagate = Misc.grid_propagate
diff --git a/Lib/tkinter/test/test_ttk/test_functions.py b/Lib/tkinter/test/test_ttk/test_functions.py
--- a/Lib/tkinter/test/test_ttk/test_functions.py
+++ b/Lib/tkinter/test/test_ttk/test_functions.py
@@ -324,26 +324,13 @@
             "-opt {3 2m}")
 
 
-    def test_dict_from_tcltuple(self):
-        fakettuple = ('-a', '{1 2 3}', '-something', 'foo')
-
-        self.assertEqual(ttk._dict_from_tcltuple(fakettuple, False),
-            {'-a': '{1 2 3}', '-something': 'foo'})
-
-        self.assertEqual(ttk._dict_from_tcltuple(fakettuple),
-            {'a': '{1 2 3}', 'something': 'foo'})
-
-        # passing a tuple with a single item should return an empty dict,
-        # since it tries to break the tuple by pairs.
-        self.assertFalse(ttk._dict_from_tcltuple(('single', )))
-
-        sspec = MockStateSpec('a', 'b')
-        self.assertEqual(ttk._dict_from_tcltuple(('-a', (sspec, 'val'))),
-            {'a': [('a', 'b', 'val')]})
-
-        self.assertEqual(ttk._dict_from_tcltuple((MockTclObj('-padding'),
-            [MockTclObj('1'), 2, MockTclObj('3m')])),
-            {'padding': [1, 2, '3m']})
+    def test_tclobj_to_py(self):
+        self.assertEqual(
+            ttk._tclobj_to_py((MockStateSpec('a', 'b'), 'val')),
+            [('a', 'b', 'val')])
+        self.assertEqual(
+            ttk._tclobj_to_py([MockTclObj('1'), 2, MockTclObj('3m')]),
+            [1, 2, '3m'])
 
 
     def test_list_from_statespec(self):
diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py
--- a/Lib/tkinter/ttk.py
+++ b/Lib/tkinter/ttk.py
@@ -26,7 +26,7 @@
            "tclobjs_to_py", "setup_master"]
 
 import tkinter
-from tkinter import _flatten, _join, _stringify
+from tkinter import _flatten, _join, _stringify, _splitdict
 
 # Verify if Tk is new enough to not need the Tile package
 _REQUIRE_TILE = True if tkinter.TkVersion < 8.5 else False
@@ -240,21 +240,6 @@
 
     return '\n'.join(script)
 
-def _dict_from_tcltuple(ttuple, cut_minus=True):
-    """Break tuple in pairs, format it properly, then build the return
-    dict. If cut_minus is True, the supposed '-' prefixing options will
-    be removed.
-
-    ttuple is expected to contain an even number of elements."""
-    opt_start = 1 if cut_minus else 0
-
-    retdict = {}
-    it = iter(ttuple)
-    for opt, val in zip(it, it):
-        retdict[str(opt)[opt_start:]] = val
-
-    return tclobjs_to_py(retdict)
-
 def _list_from_statespec(stuple):
     """Construct a list from the given statespec tuple according to the
     accepted statespec accepted by _format_mapdict."""
@@ -314,7 +299,7 @@
     if len(options) % 2: # option specified without a value, return its value
         return res
 
-    return _dict_from_tcltuple(tk.splitlist(res))
+    return _splitdict(tk, res, conv=_tclobj_to_py)
 
 def _convert_stringval(value):
     """Converts a value to, hopefully, a more appropriate Python object."""
@@ -334,20 +319,24 @@
             x = int(x)
     return x
 
+def _tclobj_to_py(val):
+    """Return value converted from Tcl object to Python object."""
+    if val and hasattr(val, '__len__') and not isinstance(val, str):
+        if getattr(val[0], 'typename', None) == 'StateSpec':
+            val = _list_from_statespec(val)
+        else:
+            val = list(map(_convert_stringval, val))
+
+    elif hasattr(val, 'typename'): # some other (single) Tcl object
+        val = _convert_stringval(val)
+
+    return val
+
 def tclobjs_to_py(adict):
     """Returns adict with its values converted from Tcl objects to Python
     objects."""
     for opt, val in adict.items():
-        if val and hasattr(val, '__len__') and not isinstance(val, str):
-            if getattr(val[0], 'typename', None) == 'StateSpec':
-                val = _list_from_statespec(val)
-            else:
-                val = list(map(_convert_stringval, val))
-
-        elif hasattr(val, 'typename'): # some other (single) Tcl object
-            val = _convert_stringval(val)
-
-        adict[opt] = val
+        adict[opt] = _tclobj_to_py(val)
 
     return adict
 
@@ -407,8 +396,10 @@
             return _list_from_statespec(self.tk.splitlist(
                 self.tk.call(self._name, "map", style, '-%s' % query_opt)))
 
-        return _dict_from_tcltuple(self.tk.splitlist(
-            self.tk.call(self._name, "map", style, *(_format_mapdict(kw)))))
+        return _splitdict(
+            self.tk,
+            self.tk.call(self._name, "map", style, *_format_mapdict(kw)),
+            conv=_tclobj_to_py)
 
 
     def lookup(self, style, option, state=None, default=None):
@@ -1425,13 +1416,16 @@
 
 
     def set(self, item, column=None, value=None):
-        """With one argument, returns a dictionary of column/value pairs
-        for the specified item. With two arguments, returns the current
-        value of the specified column. With three arguments, sets the
+        """Query or set the value of given item.
+
+        With one argument, return a dictionary of column/value pairs
+        for the specified item. With two arguments, return the current
+        value of the specified column. With three arguments, set the
         value of given column in given item to the specified value."""
         res = self.tk.call(self._w, "set", item, column, value)
         if column is None and value is None:
-            return _dict_from_tcltuple(self.tk.splitlist(res), False)
+            return _splitdict(self.tk, res,
+                              cut_minus=False, conv=_tclobj_to_py)
         else:
             return res
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -132,6 +132,9 @@
 Library
 -------
 
+- Issue #22226: First letter no longer is stripped from the "status" key in
+  the result of Treeview.heading().
+
 - Issue #19524: Fixed resource leak in the HTTP connection when an invalid
   response is received.  Patch by Martin Panter.
 

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


More information about the Python-checkins mailing list