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:
commit
87a2803eb4
|
@ -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):
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue