• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2import subprocess
3import sys
4import os
5from test import support
6from test.support import import_helper
7from test.support import os_helper
8
9# Skip this test if the _tkinter module wasn't built.
10_tkinter = import_helper.import_module('_tkinter')
11
12import tkinter
13from tkinter import Tcl
14from _tkinter import TclError
15
16try:
17    from _testcapi import INT_MAX, PY_SSIZE_T_MAX
18except ImportError:
19    INT_MAX = PY_SSIZE_T_MAX = sys.maxsize
20
21tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.')))
22
23
24class TkinterTest(unittest.TestCase):
25
26    def testFlattenLen(self):
27        # Object without length.
28        self.assertRaises(TypeError, _tkinter._flatten, True)
29        # Object with length, but not sequence.
30        self.assertRaises(TypeError, _tkinter._flatten, {})
31        # Sequence or set, but not tuple or list.
32        # (issue44608: there were leaks in the following cases)
33        self.assertRaises(TypeError, _tkinter._flatten, 'string')
34        self.assertRaises(TypeError, _tkinter._flatten, {'set'})
35
36
37class TclTest(unittest.TestCase):
38
39    def setUp(self):
40        self.interp = Tcl()
41        self.wantobjects = self.interp.tk.wantobjects()
42
43    def testEval(self):
44        tcl = self.interp
45        tcl.eval('set a 1')
46        self.assertEqual(tcl.eval('set a'),'1')
47
48    def test_eval_null_in_result(self):
49        tcl = self.interp
50        self.assertEqual(tcl.eval('set a "a\\0b"'), 'a\x00b')
51
52    def test_eval_surrogates_in_result(self):
53        tcl = self.interp
54        self.assertEqual(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>')
55
56    def testEvalException(self):
57        tcl = self.interp
58        self.assertRaises(TclError,tcl.eval,'set a')
59
60    def testEvalException2(self):
61        tcl = self.interp
62        self.assertRaises(TclError,tcl.eval,'this is wrong')
63
64    def test_eval_returns_tcl_obj(self):
65        tcl = self.interp.tk
66        tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
67        a = tcl.eval('set a')
68        expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
69        self.assertEqual(a, expected)
70
71    def testCall(self):
72        tcl = self.interp
73        tcl.call('set','a','1')
74        self.assertEqual(tcl.call('set','a'),'1')
75
76    def test_call_passing_null(self):
77        tcl = self.interp
78        tcl.call('set', 'a', 'a\0b')  # ASCII-only
79        self.assertEqual(tcl.getvar('a'), 'a\x00b')
80        self.assertEqual(tcl.call('set', 'a'), 'a\x00b')
81        self.assertEqual(tcl.eval('set a'), 'a\x00b')
82
83        tcl.call('set', 'a', '\u20ac\0')  # non-ASCII
84        self.assertEqual(tcl.getvar('a'), '\u20ac\x00')
85        self.assertEqual(tcl.call('set', 'a'), '\u20ac\x00')
86        self.assertEqual(tcl.eval('set a'), '\u20ac\x00')
87
88    def testCallException(self):
89        tcl = self.interp
90        self.assertRaises(TclError,tcl.call,'set','a')
91
92    def testCallException2(self):
93        tcl = self.interp
94        self.assertRaises(TclError,tcl.call,'this','is','wrong')
95
96    def test_call_returns_tcl_obj(self):
97        tcl = self.interp.tk
98        tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
99        a = tcl.call('set', 'a')
100        expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
101        if self.wantobjects:
102            self.assertEqual(str(a), expected)
103            self.assertEqual(a.string, expected)
104            self.assertEqual(a.typename, 'regexp')
105        else:
106            self.assertEqual(a, expected)
107
108    def testSetVar(self):
109        tcl = self.interp
110        tcl.setvar('a','1')
111        self.assertEqual(tcl.eval('set a'),'1')
112
113    def test_setvar_passing_null(self):
114        tcl = self.interp
115        tcl.setvar('a', 'a\0b')  # ASCII-only
116        self.assertEqual(tcl.getvar('a'), 'a\x00b')
117        self.assertEqual(tcl.call('set', 'a'), 'a\x00b')
118        self.assertEqual(tcl.eval('set a'), 'a\x00b')
119
120        tcl.setvar('a', '\u20ac\0')  # non-ASCII
121        self.assertEqual(tcl.getvar('a'), '\u20ac\x00')
122        self.assertEqual(tcl.call('set', 'a'), '\u20ac\x00')
123        self.assertEqual(tcl.eval('set a'), '\u20ac\x00')
124
125    def testSetVarArray(self):
126        tcl = self.interp
127        tcl.setvar('a(1)','1')
128        self.assertEqual(tcl.eval('set a(1)'),'1')
129
130    def testGetVar(self):
131        tcl = self.interp
132        tcl.eval('set a 1')
133        self.assertEqual(tcl.getvar('a'),'1')
134
135    def testGetVarArray(self):
136        tcl = self.interp
137        tcl.eval('set a(1) 1')
138        self.assertEqual(tcl.getvar('a(1)'),'1')
139
140    def testGetVarException(self):
141        tcl = self.interp
142        self.assertRaises(TclError,tcl.getvar,'a')
143
144    def testGetVarArrayException(self):
145        tcl = self.interp
146        self.assertRaises(TclError,tcl.getvar,'a(1)')
147
148    def test_getvar_returns_tcl_obj(self):
149        tcl = self.interp.tk
150        tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
151        a = tcl.getvar('a')
152        expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
153        if self.wantobjects:
154            self.assertEqual(str(a), expected)
155            self.assertEqual(a.string, expected)
156            self.assertEqual(a.typename, 'regexp')
157        else:
158            self.assertEqual(a, expected)
159
160    def testUnsetVar(self):
161        tcl = self.interp
162        tcl.setvar('a',1)
163        self.assertEqual(tcl.eval('info exists a'),'1')
164        tcl.unsetvar('a')
165        self.assertEqual(tcl.eval('info exists a'),'0')
166
167    def testUnsetVarArray(self):
168        tcl = self.interp
169        tcl.setvar('a(1)',1)
170        tcl.setvar('a(2)',2)
171        self.assertEqual(tcl.eval('info exists a(1)'),'1')
172        self.assertEqual(tcl.eval('info exists a(2)'),'1')
173        tcl.unsetvar('a(1)')
174        self.assertEqual(tcl.eval('info exists a(1)'),'0')
175        self.assertEqual(tcl.eval('info exists a(2)'),'1')
176
177    def testUnsetVarException(self):
178        tcl = self.interp
179        self.assertRaises(TclError,tcl.unsetvar,'a')
180
181    def get_integers(self):
182        return (0, 1, -1,
183                2**31-1, -2**31, 2**31, -2**31-1,
184                2**63-1, -2**63, 2**63, -2**63-1,
185                2**1000, -2**1000)
186
187    def test_getint(self):
188        tcl = self.interp.tk
189        for i in self.get_integers():
190            self.assertEqual(tcl.getint(' %d ' % i), i)
191            self.assertEqual(tcl.getint(' %#o ' % i), i)
192            # Numbers starting with 0 are parsed as decimal in Tcl 9.0
193            # and as octal in older versions.
194            self.assertEqual(tcl.getint((' %#o ' % i).replace('o', '')),
195                             i if tcl_version < (9, 0) else int('%o' % i))
196            self.assertEqual(tcl.getint(' %#x ' % i), i)
197        self.assertEqual(tcl.getint(42), 42)
198        self.assertRaises(TypeError, tcl.getint)
199        self.assertRaises(TypeError, tcl.getint, '42', '10')
200        self.assertRaises(TypeError, tcl.getint, b'42')
201        self.assertRaises(TypeError, tcl.getint, 42.0)
202        self.assertRaises(TclError, tcl.getint, 'a')
203        self.assertRaises((TypeError, ValueError, TclError),
204                          tcl.getint, '42\0')
205        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
206                          tcl.getint, '42\ud800')
207
208    def test_getdouble(self):
209        tcl = self.interp.tk
210        self.assertEqual(tcl.getdouble(' 42 '), 42.0)
211        self.assertEqual(tcl.getdouble(' 42.5 '), 42.5)
212        self.assertEqual(tcl.getdouble(42.5), 42.5)
213        self.assertEqual(tcl.getdouble(42), 42.0)
214        self.assertRaises(TypeError, tcl.getdouble)
215        self.assertRaises(TypeError, tcl.getdouble, '42.5', '10')
216        self.assertRaises(TypeError, tcl.getdouble, b'42.5')
217        self.assertRaises(TclError, tcl.getdouble, 'a')
218        self.assertRaises((TypeError, ValueError, TclError),
219                          tcl.getdouble, '42.5\0')
220        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
221                          tcl.getdouble, '42.5\ud800')
222
223    def test_getboolean(self):
224        tcl = self.interp.tk
225        self.assertIs(tcl.getboolean('on'), True)
226        self.assertIs(tcl.getboolean('1'), True)
227        self.assertIs(tcl.getboolean(42), True)
228        self.assertIs(tcl.getboolean(0), False)
229        self.assertRaises(TypeError, tcl.getboolean)
230        self.assertRaises(TypeError, tcl.getboolean, 'on', '1')
231        self.assertRaises(TypeError, tcl.getboolean, b'on')
232        self.assertRaises(TypeError, tcl.getboolean, 1.0)
233        self.assertRaises(TclError, tcl.getboolean, 'a')
234        self.assertRaises((TypeError, ValueError, TclError),
235                          tcl.getboolean, 'on\0')
236        self.assertRaises((UnicodeEncodeError, ValueError, TclError),
237                          tcl.getboolean, 'on\ud800')
238
239    def testEvalFile(self):
240        tcl = self.interp
241        filename = os_helper.TESTFN_ASCII
242        self.addCleanup(os_helper.unlink, filename)
243        with open(filename, 'w') as f:
244            f.write("""set a 1
245            set b 2
246            set c [ expr $a + $b ]
247            """)
248        tcl.evalfile(filename)
249        self.assertEqual(tcl.eval('set a'),'1')
250        self.assertEqual(tcl.eval('set b'),'2')
251        self.assertEqual(tcl.eval('set c'),'3')
252
253    def test_evalfile_null_in_result(self):
254        tcl = self.interp
255        filename = os_helper.TESTFN_ASCII
256        self.addCleanup(os_helper.unlink, filename)
257        with open(filename, 'w') as f:
258            f.write("""
259            set a "a\0b"
260            set b "a\\0b"
261            """)
262        tcl.evalfile(filename)
263        self.assertEqual(tcl.eval('set a'), 'a\x00b')
264        self.assertEqual(tcl.eval('set b'), 'a\x00b')
265
266    def test_evalfile_surrogates_in_result(self):
267        tcl = self.interp
268        encoding = tcl.call('encoding', 'system')
269        self.addCleanup(tcl.call, 'encoding', 'system', encoding)
270        tcl.call('encoding', 'system', 'utf-8')
271
272        filename = os_helper.TESTFN_ASCII
273        self.addCleanup(os_helper.unlink, filename)
274        with open(filename, 'wb') as f:
275            f.write(b"""
276            set a "<\xed\xa0\xbd\xed\xb2\xbb>"
277            """)
278        if tcl_version >= (9, 0):
279            self.assertRaises(TclError, tcl.evalfile, filename)
280        else:
281            tcl.evalfile(filename)
282            self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>')
283
284        with open(filename, 'wb') as f:
285            f.write(b"""
286            set b "<\\ud83d\\udcbb>"
287            """)
288        tcl.evalfile(filename)
289        self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>')
290
291    def testEvalFileException(self):
292        tcl = self.interp
293        filename = "doesnotexists"
294        try:
295            os.remove(filename)
296        except Exception as e:
297            pass
298        self.assertRaises(TclError,tcl.evalfile,filename)
299
300    def testPackageRequireException(self):
301        tcl = self.interp
302        self.assertRaises(TclError,tcl.eval,'package require DNE')
303
304    @unittest.skipUnless(sys.platform == 'win32', 'Requires Windows')
305    def testLoadWithUNC(self):
306        # Build a UNC path from the regular path.
307        # Something like
308        #   \\%COMPUTERNAME%\c$\python27\python.exe
309
310        fullname = os.path.abspath(sys.executable)
311        if fullname[1] != ':':
312            raise unittest.SkipTest('Absolute path should have drive part')
313        unc_name = r'\\%s\%s$\%s' % (os.environ['COMPUTERNAME'],
314                                    fullname[0],
315                                    fullname[3:])
316        if not os.path.exists(unc_name):
317            raise unittest.SkipTest('Cannot connect to UNC Path')
318
319        with os_helper.EnvironmentVarGuard() as env:
320            env.unset("TCL_LIBRARY")
321            stdout = subprocess.check_output(
322                    [unc_name, '-c', 'import tkinter; print(tkinter)'])
323
324        self.assertIn(b'tkinter', stdout)
325
326    def test_exprstring(self):
327        tcl = self.interp
328        tcl.call('set', 'a', 3)
329        tcl.call('set', 'b', 6)
330        def check(expr, expected):
331            result = tcl.exprstring(expr)
332            self.assertEqual(result, expected)
333            self.assertIsInstance(result, str)
334
335        self.assertRaises(TypeError, tcl.exprstring)
336        self.assertRaises(TypeError, tcl.exprstring, '8.2', '+6')
337        self.assertRaises(TypeError, tcl.exprstring, b'8.2 + 6')
338        self.assertRaises(TclError, tcl.exprstring, 'spam')
339        check('', '0')
340        check('8.2 + 6', '14.2')
341        check('3.1 + $a', '6.1')
342        check('2 + "$a.$b"', '5.6')
343        check('4*[llength "6 2"]', '8')
344        check('{word one} < "word $a"', '0')
345        check('4*2 < 7', '0')
346        check('hypot($a, 4)', '5.0')
347        check('5 / 4', '1')
348        check('5 / 4.0', '1.25')
349        check('5 / ( [string length "abcd"] + 0.0 )', '1.25')
350        check('20.0/5.0', '4.0')
351        check('"0x03" > "2"', '1')
352        check('[string length "a\xbd\u20ac"]', '3')
353        check(r'[string length "a\xbd\u20ac"]', '3')
354        check('"abc"', 'abc')
355        check('"a\xbd\u20ac"', 'a\xbd\u20ac')
356        check(r'"a\xbd\u20ac"', 'a\xbd\u20ac')
357        check(r'"a\0b"', 'a\x00b')
358        check('2**64', str(2**64))
359
360    def test_exprdouble(self):
361        tcl = self.interp
362        tcl.call('set', 'a', 3)
363        tcl.call('set', 'b', 6)
364        def check(expr, expected):
365            result = tcl.exprdouble(expr)
366            self.assertEqual(result, expected)
367            self.assertIsInstance(result, float)
368
369        self.assertRaises(TypeError, tcl.exprdouble)
370        self.assertRaises(TypeError, tcl.exprdouble, '8.2', '+6')
371        self.assertRaises(TypeError, tcl.exprdouble, b'8.2 + 6')
372        self.assertRaises(TclError, tcl.exprdouble, 'spam')
373        check('', 0.0)
374        check('8.2 + 6', 14.2)
375        check('3.1 + $a', 6.1)
376        check('2 + "$a.$b"', 5.6)
377        check('4*[llength "6 2"]', 8.0)
378        check('{word one} < "word $a"', 0.0)
379        check('4*2 < 7', 0.0)
380        check('hypot($a, 4)', 5.0)
381        check('5 / 4', 1.0)
382        check('5 / 4.0', 1.25)
383        check('5 / ( [string length "abcd"] + 0.0 )', 1.25)
384        check('20.0/5.0', 4.0)
385        check('"0x03" > "2"', 1.0)
386        check('[string length "a\xbd\u20ac"]', 3.0)
387        check(r'[string length "a\xbd\u20ac"]', 3.0)
388        self.assertRaises(TclError, tcl.exprdouble, '"abc"')
389        check('2**64', float(2**64))
390
391    def test_exprlong(self):
392        tcl = self.interp
393        tcl.call('set', 'a', 3)
394        tcl.call('set', 'b', 6)
395        def check(expr, expected):
396            result = tcl.exprlong(expr)
397            self.assertEqual(result, expected)
398            self.assertIsInstance(result, int)
399
400        self.assertRaises(TypeError, tcl.exprlong)
401        self.assertRaises(TypeError, tcl.exprlong, '8.2', '+6')
402        self.assertRaises(TypeError, tcl.exprlong, b'8.2 + 6')
403        self.assertRaises(TclError, tcl.exprlong, 'spam')
404        check('', 0)
405        check('8.2 + 6', 14)
406        check('3.1 + $a', 6)
407        check('2 + "$a.$b"', 5)
408        check('4*[llength "6 2"]', 8)
409        check('{word one} < "word $a"', 0)
410        check('4*2 < 7', 0)
411        check('hypot($a, 4)', 5)
412        check('5 / 4', 1)
413        check('5 / 4.0', 1)
414        check('5 / ( [string length "abcd"] + 0.0 )', 1)
415        check('20.0/5.0', 4)
416        check('"0x03" > "2"', 1)
417        check('[string length "a\xbd\u20ac"]', 3)
418        check(r'[string length "a\xbd\u20ac"]', 3)
419        self.assertRaises(TclError, tcl.exprlong, '"abc"')
420        self.assertRaises(TclError, tcl.exprlong, '2**64')
421
422    def test_exprboolean(self):
423        tcl = self.interp
424        tcl.call('set', 'a', 3)
425        tcl.call('set', 'b', 6)
426        def check(expr, expected):
427            result = tcl.exprboolean(expr)
428            self.assertEqual(result, expected)
429            self.assertIsInstance(result, int)
430            self.assertNotIsInstance(result, bool)
431
432        self.assertRaises(TypeError, tcl.exprboolean)
433        self.assertRaises(TypeError, tcl.exprboolean, '8.2', '+6')
434        self.assertRaises(TypeError, tcl.exprboolean, b'8.2 + 6')
435        self.assertRaises(TclError, tcl.exprboolean, 'spam')
436        check('', False)
437        for value in ('0', 'false', 'no', 'off'):
438            check(value, False)
439            check('"%s"' % value, False)
440            check('{%s}' % value, False)
441        for value in ('1', 'true', 'yes', 'on'):
442            check(value, True)
443            check('"%s"' % value, True)
444            check('{%s}' % value, True)
445        check('8.2 + 6', True)
446        check('3.1 + $a', True)
447        check('2 + "$a.$b"', True)
448        check('4*[llength "6 2"]', True)
449        check('{word one} < "word $a"', False)
450        check('4*2 < 7', False)
451        check('hypot($a, 4)', True)
452        check('5 / 4', True)
453        check('5 / 4.0', True)
454        check('5 / ( [string length "abcd"] + 0.0 )', True)
455        check('20.0/5.0', True)
456        check('"0x03" > "2"', True)
457        check('[string length "a\xbd\u20ac"]', True)
458        check(r'[string length "a\xbd\u20ac"]', True)
459        self.assertRaises(TclError, tcl.exprboolean, '"abc"')
460        check('2**64', True)
461
462    def test_booleans(self):
463        tcl = self.interp
464        def check(expr, expected):
465            result = tcl.call('expr', expr)
466            if tcl.wantobjects():
467                self.assertEqual(result, expected)
468                self.assertIsInstance(result, int)
469            else:
470                self.assertIn(result, (expr, str(int(expected))))
471                self.assertIsInstance(result, str)
472        check('true', True)
473        check('yes', True)
474        check('on', True)
475        check('false', False)
476        check('no', False)
477        check('off', False)
478        check('1 < 2', True)
479        check('1 > 2', False)
480
481    def test_expr_bignum(self):
482        tcl = self.interp
483        for i in self.get_integers():
484            result = tcl.call('expr', str(i))
485            if self.wantobjects:
486                self.assertEqual(result, i)
487                self.assertIsInstance(result, int)
488            else:
489                self.assertEqual(result, str(i))
490                self.assertIsInstance(result, str)
491
492    def test_passing_values(self):
493        def passValue(value):
494            return self.interp.call('set', '_', value)
495
496        self.assertEqual(passValue(True), True if self.wantobjects else '1')
497        self.assertEqual(passValue(False), False if self.wantobjects else '0')
498        self.assertEqual(passValue('string'), 'string')
499        self.assertEqual(passValue('string\u20ac'), 'string\u20ac')
500        self.assertEqual(passValue('string\U0001f4bb'), 'string\U0001f4bb')
501        self.assertEqual(passValue('str\x00ing'), 'str\x00ing')
502        self.assertEqual(passValue('str\x00ing\xbd'), 'str\x00ing\xbd')
503        self.assertEqual(passValue('str\x00ing\u20ac'), 'str\x00ing\u20ac')
504        self.assertEqual(passValue('str\x00ing\U0001f4bb'),
505                         'str\x00ing\U0001f4bb')
506        if sys.platform != 'win32':
507            self.assertEqual(passValue('<\udce2\udc82\udcac>'),
508                             '<\u20ac>')
509            self.assertEqual(passValue('<\udced\udca0\udcbd\udced\udcb2\udcbb>'),
510                             '<\U0001f4bb>')
511        self.assertEqual(passValue(b'str\x00ing'),
512                         b'str\x00ing' if self.wantobjects else 'str\x00ing')
513        self.assertEqual(passValue(b'str\xc0\x80ing'),
514                         b'str\xc0\x80ing' if self.wantobjects else 'str\xc0\x80ing')
515        self.assertEqual(passValue(b'str\xbding'),
516                         b'str\xbding' if self.wantobjects else 'str\xbding')
517        for i in self.get_integers():
518            self.assertEqual(passValue(i), i if self.wantobjects else str(i))
519        for f in (0.0, 1.0, -1.0, 1/3,
520                  sys.float_info.min, sys.float_info.max,
521                  -sys.float_info.min, -sys.float_info.max):
522            if self.wantobjects:
523                self.assertEqual(passValue(f), f)
524            else:
525                self.assertEqual(float(passValue(f)), f)
526        if self.wantobjects:
527            f = passValue(float('nan'))
528            self.assertNotEqual(f, f)
529            self.assertEqual(passValue(float('inf')), float('inf'))
530            self.assertEqual(passValue(-float('inf')), -float('inf'))
531        else:
532            self.assertEqual(float(passValue(float('inf'))), float('inf'))
533            self.assertEqual(float(passValue(-float('inf'))), -float('inf'))
534            # XXX NaN representation can be not parsable by float()
535        self.assertEqual(passValue((1, '2', (3.4,))),
536                         (1, '2', (3.4,)) if self.wantobjects else '1 2 3.4')
537        self.assertEqual(passValue(['a', ['b', 'c']]),
538                         ('a', ('b', 'c')) if self.wantobjects else 'a {b c}')
539
540    def test_user_command(self):
541        result = None
542        def testfunc(arg):
543            nonlocal result
544            result = arg
545            return arg
546        self.interp.createcommand('testfunc', testfunc)
547        self.addCleanup(self.interp.tk.deletecommand, 'testfunc')
548        def check(value, expected1=None, expected2=None, *, eq=self.assertEqual):
549            expected = value
550            if self.wantobjects >= 2:
551                if expected2 is not None:
552                    expected = expected2
553                expected_type = type(expected)
554            else:
555                if expected1 is not None:
556                    expected = expected1
557                expected_type = str
558            nonlocal result
559            result = None
560            r = self.interp.call('testfunc', value)
561            self.assertIsInstance(result, expected_type)
562            eq(result, expected)
563            self.assertIsInstance(r, expected_type)
564            eq(r, expected)
565        def float_eq(actual, expected):
566            self.assertAlmostEqual(float(actual), expected,
567                                   delta=abs(expected) * 1e-10)
568
569        check(True, '1', 1)
570        check(False, '0', 0)
571        check('string')
572        check('string\xbd')
573        check('string\u20ac')
574        check('string\U0001f4bb')
575        if sys.platform != 'win32':
576            check('<\udce2\udc82\udcac>', '<\u20ac>', '<\u20ac>')
577            check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>', '<\U0001f4bb>')
578        check('')
579        check(b'string', 'string')
580        check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
581        check(b'string\xbd', 'string\xbd')
582        check(b'', '')
583        check('str\x00ing')
584        check('str\x00ing\xbd')
585        check('str\x00ing\u20ac')
586        check(b'str\x00ing', 'str\x00ing')
587        check(b'str\xc0\x80ing', 'str\xc0\x80ing')
588        check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac')
589        for i in self.get_integers():
590            check(i, str(i))
591        for f in (0.0, 1.0, -1.0):
592            check(f, repr(f))
593        for f in (1/3.0, sys.float_info.min, sys.float_info.max,
594                  -sys.float_info.min, -sys.float_info.max):
595            check(f, eq=float_eq)
596        check(float('inf'), eq=float_eq)
597        check(-float('inf'), eq=float_eq)
598        # XXX NaN representation can be not parsable by float()
599        check((), '', '')
600        check((1, (2,), (3, 4), '5 6', ()),
601              '1 2 {3 4} {5 6} {}',
602              (1, (2,), (3, 4), '5 6', ''))
603        check([1, [2,], [3, 4], '5 6', []],
604              '1 2 {3 4} {5 6} {}',
605              (1, (2,), (3, 4), '5 6', ''))
606
607    def test_passing_tcl_obj(self):
608        tcl = self.interp.tk
609        a = None
610        def testfunc(arg):
611            nonlocal a
612            a = arg
613        self.interp.createcommand('testfunc', testfunc)
614        self.addCleanup(self.interp.tk.deletecommand, 'testfunc')
615        tcl.eval(r'set a "\u20ac \ud83d\udcbb \0 \udcab"; regexp -about $a')
616        tcl.eval(r'testfunc $a')
617        expected = '\u20ac \U0001f4bb \0 \udced\udcb2\udcab'
618        if self.wantobjects >= 2:
619            self.assertEqual(str(a), expected)
620            self.assertEqual(a.string, expected)
621            self.assertEqual(a.typename, 'regexp')
622        else:
623            self.assertEqual(a, expected)
624
625    def test_splitlist(self):
626        splitlist = self.interp.tk.splitlist
627        call = self.interp.tk.call
628        self.assertRaises(TypeError, splitlist)
629        self.assertRaises(TypeError, splitlist, 'a', 'b')
630        self.assertRaises(TypeError, splitlist, 2)
631        testcases = [
632            ('2', ('2',)),
633            ('', ()),
634            ('{}', ('',)),
635            ('""', ('',)),
636            ('a\n b\t\r c\n ', ('a', 'b', 'c')),
637            (b'a\n b\t\r c\n ', ('a', 'b', 'c')),
638            ('a \u20ac', ('a', '\u20ac')),
639            ('a \U0001f4bb', ('a', '\U0001f4bb')),
640            (b'a \xe2\x82\xac', ('a', '\u20ac')),
641            (b'a \xf0\x9f\x92\xbb', ('a', '\U0001f4bb')),
642            (b'a \xed\xa0\xbd\xed\xb2\xbb', ('a', '\U0001f4bb')),
643            (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')),
644            ('a {b c}', ('a', 'b c')),
645            (r'a b\ c', ('a', 'b c')),
646            (('a', 'b c'), ('a', 'b c')),
647            ('a 2', ('a', '2')),
648            (('a', 2), ('a', 2)),
649            ('a 3.4', ('a', '3.4')),
650            (('a', 3.4), ('a', 3.4)),
651            ((), ()),
652            ([], ()),
653            (['a', ['b', 'c']], ('a', ['b', 'c'])),
654            (call('list', 1, '2', (3.4,)),
655                (1, '2', (3.4,)) if self.wantobjects else
656                ('1', '2', '3.4')),
657        ]
658        if not self.wantobjects:
659            expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
660        else:
661            expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
662        testcases += [
663            (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
664                expected),
665        ]
666        dbg_info = ('want objects? %s, Tcl version: %s, Tcl patchlevel: %s'
667                    % (self.wantobjects, tcl_version, self.interp.info_patchlevel()))
668        for arg, res in testcases:
669            self.assertEqual(splitlist(arg), res,
670                             'arg=%a, %s' % (arg, dbg_info))
671        self.assertRaises(TclError, splitlist, '{')
672
673    def test_splitdict(self):
674        splitdict = tkinter._splitdict
675        tcl = self.interp.tk
676
677        arg = '-a {1 2 3} -something foo status {}'
678        self.assertEqual(splitdict(tcl, arg, False),
679            {'-a': '1 2 3', '-something': 'foo', 'status': ''})
680        self.assertEqual(splitdict(tcl, arg),
681            {'a': '1 2 3', 'something': 'foo', 'status': ''})
682
683        arg = ('-a', (1, 2, 3), '-something', 'foo', 'status', '{}')
684        self.assertEqual(splitdict(tcl, arg, False),
685            {'-a': (1, 2, 3), '-something': 'foo', 'status': '{}'})
686        self.assertEqual(splitdict(tcl, arg),
687            {'a': (1, 2, 3), 'something': 'foo', 'status': '{}'})
688
689        self.assertRaises(RuntimeError, splitdict, tcl, '-a b -c ')
690        self.assertRaises(RuntimeError, splitdict, tcl, ('-a', 'b', '-c'))
691
692        arg = tcl.call('list',
693                        '-a', (1, 2, 3), '-something', 'foo', 'status', ())
694        self.assertEqual(splitdict(tcl, arg),
695            {'a': (1, 2, 3) if self.wantobjects else '1 2 3',
696             'something': 'foo', 'status': ''})
697
698        arg = tcl.call('dict', 'create',
699                       '-a', (1, 2, 3), '-something', 'foo', 'status', ())
700        if not self.wantobjects:
701            expected = {'a': '1 2 3', 'something': 'foo', 'status': ''}
702        else:
703            expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
704        self.assertEqual(splitdict(tcl, arg), expected)
705
706    def test_join(self):
707        join = tkinter._join
708        tcl = self.interp.tk
709        def unpack(s):
710            return tcl.call('lindex', s, 0)
711        def check(value):
712            self.assertEqual(unpack(join([value])), value)
713            self.assertEqual(unpack(join([value, 0])), value)
714            self.assertEqual(unpack(unpack(join([[value]]))), value)
715            self.assertEqual(unpack(unpack(join([[value, 0]]))), value)
716            self.assertEqual(unpack(unpack(join([[value], 0]))), value)
717            self.assertEqual(unpack(unpack(join([[value, 0], 0]))), value)
718        check('')
719        check('spam')
720        check('sp am')
721        check('sp\tam')
722        check('sp\nam')
723        check(' \t\n')
724        check('{spam}')
725        check('{sp am}')
726        check('"spam"')
727        check('"sp am"')
728        check('{"spam"}')
729        check('"{spam}"')
730        check('sp\\am')
731        check('"sp\\am"')
732        check('"{}" "{}"')
733        check('"\\')
734        check('"{')
735        check('"}')
736        check('\n\\')
737        check('\n{')
738        check('\n}')
739        check('\\\n')
740        check('{\n')
741        check('}\n')
742
743    @support.cpython_only
744    def test_new_tcl_obj(self):
745        support.check_disallow_instantiation(self, _tkinter.Tcl_Obj)
746        support.check_disallow_instantiation(self, _tkinter.TkttType)
747        support.check_disallow_instantiation(self, _tkinter.TkappType)
748
749
750class BigmemTclTest(unittest.TestCase):
751
752    def setUp(self):
753        self.interp = Tcl()
754
755    @support.cpython_only
756    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
757    @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False)
758    def test_huge_string_call(self, size):
759        value = ' ' * size
760        self.assertRaises(OverflowError, self.interp.call, 'string', 'index', value, 0)
761
762    @support.cpython_only
763    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
764    @support.bigmemtest(size=INT_MAX + 1, memuse=2, dry_run=False)
765    def test_huge_string_builtins(self, size):
766        tk = self.interp.tk
767        value = '1' + ' ' * size
768        self.assertRaises(OverflowError, tk.getint, value)
769        self.assertRaises(OverflowError, tk.getdouble, value)
770        self.assertRaises(OverflowError, tk.getboolean, value)
771        self.assertRaises(OverflowError, tk.eval, value)
772        self.assertRaises(OverflowError, tk.evalfile, value)
773        self.assertRaises(OverflowError, tk.record, value)
774        self.assertRaises(OverflowError, tk.adderrorinfo, value)
775        self.assertRaises(OverflowError, tk.setvar, value, 'x', 'a')
776        self.assertRaises(OverflowError, tk.setvar, 'x', value, 'a')
777        self.assertRaises(OverflowError, tk.unsetvar, value)
778        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
779        self.assertRaises(OverflowError, tk.adderrorinfo, value)
780        self.assertRaises(OverflowError, tk.exprstring, value)
781        self.assertRaises(OverflowError, tk.exprlong, value)
782        self.assertRaises(OverflowError, tk.exprboolean, value)
783        self.assertRaises(OverflowError, tk.splitlist, value)
784        self.assertRaises(OverflowError, tk.createcommand, value, max)
785        self.assertRaises(OverflowError, tk.deletecommand, value)
786
787    @support.cpython_only
788    @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX")
789    @support.bigmemtest(size=INT_MAX + 1, memuse=6, dry_run=False)
790    def test_huge_string_builtins2(self, size):
791        # These commands require larger memory for possible error messages
792        tk = self.interp.tk
793        value = '1' + ' ' * size
794        self.assertRaises(OverflowError, tk.evalfile, value)
795        self.assertRaises(OverflowError, tk.unsetvar, value)
796        self.assertRaises(OverflowError, tk.unsetvar, 'x', value)
797
798
799def setUpModule():
800    if support.verbose:
801        tcl = Tcl()
802        print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True)
803
804
805if __name__ == "__main__":
806    unittest.main()
807