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