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