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