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_split(self): 621 split = self.interp.tk.split 622 call = self.interp.tk.call 623 with warnings.catch_warnings(): 624 warnings.filterwarnings('ignore', r'\bsplit\b.*\bsplitlist\b', 625 DeprecationWarning) 626 self.assertRaises(TypeError, split) 627 self.assertRaises(TypeError, split, 'a', 'b') 628 self.assertRaises(TypeError, split, 2) 629 testcases = [ 630 ('2', '2'), 631 ('', ''), 632 ('{}', ''), 633 ('""', ''), 634 ('{', '{'), 635 ('a\n b\t\r c\n ', ('a', 'b', 'c')), 636 (b'a\n b\t\r c\n ', ('a', 'b', 'c')), 637 ('a \u20ac', ('a', '\u20ac')), 638 (b'a \xe2\x82\xac', ('a', '\u20ac')), 639 (b'a\xc0\x80b', 'a\x00b'), 640 (b'a\xc0\x80b c\xc0\x80d', ('a\x00b', 'c\x00d')), 641 (b'{a\xc0\x80b c\xc0\x80d', '{a\x00b c\x00d'), 642 ('a {b c}', ('a', ('b', 'c'))), 643 (r'a b\ c', ('a', ('b', 'c'))), 644 (('a', b'b c'), ('a', ('b', 'c'))), 645 (('a', 'b c'), ('a', ('b', 'c'))), 646 ('a 2', ('a', '2')), 647 (('a', 2), ('a', 2)), 648 ('a 3.4', ('a', '3.4')), 649 (('a', 3.4), ('a', 3.4)), 650 (('a', (2, 3.4)), ('a', (2, 3.4))), 651 ((), ()), 652 ([], ()), 653 (['a', 'b c'], ('a', ('b', 'c'))), 654 (['a', ['b', 'c']], ('a', ('b', 'c'))), 655 (call('list', 1, '2', (3.4,)), 656 (1, '2', (3.4,)) if self.wantobjects else 657 ('1', '2', '3.4')), 658 ] 659 if tcl_version >= (8, 5): 660 if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): 661 # Before 8.5.5 dicts were converted to lists through string 662 expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') 663 else: 664 expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,)) 665 testcases += [ 666 (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), 667 expected), 668 ] 669 for arg, res in testcases: 670 with self.assertWarns(DeprecationWarning): 671 self.assertEqual(split(arg), res, msg=arg) 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 if tcl_version >= (8, 5): 699 arg = tcl.call('dict', 'create', 700 '-a', (1, 2, 3), '-something', 'foo', 'status', ()) 701 if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): 702 # Before 8.5.5 dicts were converted to lists through string 703 expected = {'a': '1 2 3', 'something': 'foo', 'status': ''} 704 else: 705 expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''} 706 self.assertEqual(splitdict(tcl, arg), expected) 707 708 def test_join(self): 709 join = tkinter._join 710 tcl = self.interp.tk 711 def unpack(s): 712 return tcl.call('lindex', s, 0) 713 def check(value): 714 self.assertEqual(unpack(join([value])), value) 715 self.assertEqual(unpack(join([value, 0])), value) 716 self.assertEqual(unpack(unpack(join([[value]]))), value) 717 self.assertEqual(unpack(unpack(join([[value, 0]]))), value) 718 self.assertEqual(unpack(unpack(join([[value], 0]))), value) 719 self.assertEqual(unpack(unpack(join([[value, 0], 0]))), value) 720 check('') 721 check('spam') 722 check('sp am') 723 check('sp\tam') 724 check('sp\nam') 725 check(' \t\n') 726 check('{spam}') 727 check('{sp am}') 728 check('"spam"') 729 check('"sp am"') 730 check('{"spam"}') 731 check('"{spam}"') 732 check('sp\\am') 733 check('"sp\\am"') 734 check('"{}" "{}"') 735 check('"\\') 736 check('"{') 737 check('"}') 738 check('\n\\') 739 check('\n{') 740 check('\n}') 741 check('\\\n') 742 check('{\n') 743 check('}\n') 744 745 @support.cpython_only 746 def test_new_tcl_obj(self): 747 support.check_disallow_instantiation(self, _tkinter.Tcl_Obj) 748 support.check_disallow_instantiation(self, _tkinter.TkttType) 749 support.check_disallow_instantiation(self, _tkinter.TkappType) 750 751class BigmemTclTest(unittest.TestCase): 752 753 def setUp(self): 754 self.interp = Tcl() 755 756 @support.cpython_only 757 @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") 758 @support.bigmemtest(size=INT_MAX + 1, memuse=5, dry_run=False) 759 def test_huge_string_call(self, size): 760 value = ' ' * size 761 self.assertRaises(OverflowError, self.interp.call, 'string', 'index', value, 0) 762 763 @support.cpython_only 764 @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") 765 @support.bigmemtest(size=INT_MAX + 1, memuse=2, dry_run=False) 766 def test_huge_string_builtins(self, size): 767 tk = self.interp.tk 768 value = '1' + ' ' * size 769 self.assertRaises(OverflowError, tk.getint, value) 770 self.assertRaises(OverflowError, tk.getdouble, value) 771 self.assertRaises(OverflowError, tk.getboolean, value) 772 self.assertRaises(OverflowError, tk.eval, value) 773 self.assertRaises(OverflowError, tk.evalfile, value) 774 self.assertRaises(OverflowError, tk.record, value) 775 self.assertRaises(OverflowError, tk.adderrorinfo, value) 776 self.assertRaises(OverflowError, tk.setvar, value, 'x', 'a') 777 self.assertRaises(OverflowError, tk.setvar, 'x', value, 'a') 778 self.assertRaises(OverflowError, tk.unsetvar, value) 779 self.assertRaises(OverflowError, tk.unsetvar, 'x', value) 780 self.assertRaises(OverflowError, tk.adderrorinfo, value) 781 self.assertRaises(OverflowError, tk.exprstring, value) 782 self.assertRaises(OverflowError, tk.exprlong, value) 783 self.assertRaises(OverflowError, tk.exprboolean, value) 784 self.assertRaises(OverflowError, tk.splitlist, value) 785 self.assertRaises(OverflowError, tk.split, value) 786 self.assertRaises(OverflowError, tk.createcommand, value, max) 787 self.assertRaises(OverflowError, tk.deletecommand, value) 788 789 @support.cpython_only 790 @unittest.skipUnless(INT_MAX < PY_SSIZE_T_MAX, "needs UINT_MAX < SIZE_MAX") 791 @support.bigmemtest(size=INT_MAX + 1, memuse=6, dry_run=False) 792 def test_huge_string_builtins2(self, size): 793 # These commands require larger memory for possible error messages 794 tk = self.interp.tk 795 value = '1' + ' ' * size 796 self.assertRaises(OverflowError, tk.evalfile, value) 797 self.assertRaises(OverflowError, tk.unsetvar, value) 798 self.assertRaises(OverflowError, tk.unsetvar, 'x', value) 799 800 801def setUpModule(): 802 if support.verbose: 803 tcl = Tcl() 804 print('patchlevel =', tcl.call('info', 'patchlevel')) 805 806 807if __name__ == "__main__": 808 unittest.main() 809