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().
This commit is contained in:
Serhiy Storchaka 2014-09-06 22:49:07 +03:00
commit 87a2803eb4
5 changed files with 112 additions and 93 deletions

View File

@ -7,7 +7,7 @@ from test import support
_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 @@ class TclTest(unittest.TestCase):
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):

View File

@ -112,6 +112,29 @@ def _cnfmerge(cnfs):
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 @@ class Misc:
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 @@ class Pack:
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 @@ class Place:
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 @@ class Grid:
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

View File

@ -324,26 +324,13 @@ class InternalFunctionsTest(unittest.TestCase):
"-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):

View File

@ -26,7 +26,7 @@ __all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label",
"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 @@ def _script_from_settings(settings):
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 @@ def _val_or_dict(tk, options, *args):
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 @@ def _to_number(x):
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 @@ class Style(object):
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 @@ class Treeview(Widget, tkinter.XView, tkinter.YView):
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

View File

@ -132,6 +132,9 @@ Core and Builtins
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.