Coverage for idle_test/test_pyparse.py: 99%

210 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-11 13:22 -0700

1"Test pyparse, coverage 96%." 

2 

3from idlelib import pyparse 

4import unittest 

5from collections import namedtuple 

6 

7 

8class ParseMapTest(unittest.TestCase): 

9 

10 def test_parsemap(self): 

11 keepwhite = {ord(c): ord(c) for c in ' \t\n\r'} 1o

12 mapping = pyparse.ParseMap(keepwhite) 1o

13 self.assertEqual(mapping[ord('\t')], ord('\t')) 1o

14 self.assertEqual(mapping[ord('a')], ord('x')) 1o

15 self.assertEqual(mapping[1000], ord('x')) 1o

16 

17 def test_trans(self): 

18 # trans is the production instance of ParseMap, used in _study1 

19 parser = pyparse.Parser(4, 4) 1p

20 self.assertEqual('\t a([{b}])b"c\'d\n'.translate(pyparse.trans), 1p

21 'xxx(((x)))x"x\'x\n') 

22 

23 

24class PyParseTest(unittest.TestCase): 

25 

26 @classmethod 

27 def setUpClass(cls): 

28 cls.parser = pyparse.Parser(indentwidth=4, tabwidth=4) 

29 

30 @classmethod 

31 def tearDownClass(cls): 

32 del cls.parser 

33 

34 def test_init(self): 

35 self.assertEqual(self.parser.indentwidth, 4) 1q

36 self.assertEqual(self.parser.tabwidth, 4) 1q

37 

38 def test_set_code(self): 

39 eq = self.assertEqual 1h

40 p = self.parser 1h

41 setcode = p.set_code 1h

42 

43 # Not empty and doesn't end with newline. 

44 with self.assertRaises(AssertionError): 1h

45 setcode('a') 1h

46 

47 tests = ('', 1h

48 'a\n') 

49 

50 for string in tests: 1h

51 with self.subTest(string=string): 1h

52 setcode(string) 1h

53 eq(p.code, string) 1h

54 eq(p.study_level, 0) 1h

55 

56 def test_find_good_parse_start(self): 

57 eq = self.assertEqual 1b

58 p = self.parser 1b

59 setcode = p.set_code 1b

60 start = p.find_good_parse_start 1b

61 def char_in_string_false(index): return False 1b

62 

63 # First line starts with 'def' and ends with ':', then 0 is the pos. 

64 setcode('def spam():\n') 1b

65 eq(start(char_in_string_false), 0) 1b

66 

67 # First line begins with a keyword in the list and ends 

68 # with an open brace, then 0 is the pos. This is how 

69 # hyperparser calls this function as the newline is not added 

70 # in the editor, but rather on the call to setcode. 

71 setcode('class spam( ' + ' \n') 1b

72 eq(start(char_in_string_false), 0) 1b

73 

74 # Split def across lines. 

75 setcode('"""This is a module docstring"""\n' 1b

76 'class C:\n' 

77 ' def __init__(self, a,\n' 

78 ' b=True):\n' 

79 ' pass\n' 

80 ) 

81 pos0, pos = 33, 42 # Start of 'class...', ' def' lines. 1b

82 

83 # Passing no value or non-callable should fail (issue 32989). 

84 with self.assertRaises(TypeError): 1b

85 start() 1b

86 with self.assertRaises(TypeError): 1b

87 start(False) 1b

88 

89 # Make text look like a string. This returns pos as the start 

90 # position, but it's set to None. 

91 self.assertIsNone(start(is_char_in_string=lambda index: True)) 1b

92 

93 # Make all text look like it's not in a string. This means that it 

94 # found a good start position. 

95 eq(start(char_in_string_false), pos) 1b

96 

97 # If the beginning of the def line is not in a string, then it 

98 # returns that as the index. 

99 eq(start(is_char_in_string=lambda index: index > pos), pos) 1b

100 # If the beginning of the def line is in a string, then it 

101 # looks for a previous index. 

102 eq(start(is_char_in_string=lambda index: index >= pos), pos0) 1b

103 # If everything before the 'def' is in a string, then returns None. 

104 # The non-continuation def line returns 44 (see below). 

105 eq(start(is_char_in_string=lambda index: index < pos), None) 1b

106 

107 # Code without extra line break in def line - mostly returns the same 

108 # values. 

109 setcode('"""This is a module docstring"""\n' 1b

110 'class C:\n' 

111 ' def __init__(self, a, b=True):\n' 

112 ' pass\n' 

113 ) # Does not affect class, def positions. 

114 eq(start(char_in_string_false), pos) 1b

115 eq(start(is_char_in_string=lambda index: index > pos), pos) 1b

116 eq(start(is_char_in_string=lambda index: index >= pos), pos0) 1b

117 # When the def line isn't split, this returns which doesn't match the 

118 # split line test. 

119 eq(start(is_char_in_string=lambda index: index < pos), pos) 1b

120 

121 def test_set_lo(self): 

122 code = ( 1l

123 '"""This is a module docstring"""\n' 

124 'class C:\n' 

125 ' def __init__(self, a,\n' 

126 ' b=True):\n' 

127 ' pass\n' 

128 ) 

129 pos = 42 1l

130 p = self.parser 1l

131 p.set_code(code) 1l

132 

133 # Previous character is not a newline. 

134 with self.assertRaises(AssertionError): 1l

135 p.set_lo(5) 1l

136 

137 # A value of 0 doesn't change self.code. 

138 p.set_lo(0) 1l

139 self.assertEqual(p.code, code) 1l

140 

141 # An index that is preceded by a newline. 

142 p.set_lo(pos) 1l

143 self.assertEqual(p.code, code[pos:]) 1l

144 

145 def test_study1(self): 

146 eq = self.assertEqual 1e

147 p = self.parser 1e

148 setcode = p.set_code 1e

149 study = p._study1 1e

150 

151 (NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5) 1e

152 TestInfo = namedtuple('TestInfo', ['string', 'goodlines', 1e

153 'continuation']) 

154 tests = ( 1e

155 TestInfo('', [0], NONE), 

156 # Docstrings. 

157 TestInfo('"""This is a complete docstring."""\n', [0, 1], NONE), 

158 TestInfo("'''This is a complete docstring.'''\n", [0, 1], NONE), 

159 TestInfo('"""This is a continued docstring.\n', [0, 1], FIRST), 

160 TestInfo("'''This is a continued docstring.\n", [0, 1], FIRST), 

161 TestInfo('"""Closing quote does not match."\n', [0, 1], FIRST), 

162 TestInfo('"""Bracket in docstring [\n', [0, 1], FIRST), 

163 TestInfo("'''Incomplete two line docstring.\n\n", [0, 2], NEXT), 

164 # Single-quoted strings. 

165 TestInfo('"This is a complete string."\n', [0, 1], NONE), 

166 TestInfo('"This is an incomplete string.\n', [0, 1], NONE), 

167 TestInfo("'This is more incomplete.\n\n", [0, 1, 2], NONE), 

168 # Comment (backslash does not continue comments). 

169 TestInfo('# Comment\\\n', [0, 1], NONE), 

170 # Brackets. 

171 TestInfo('("""Complete string in bracket"""\n', [0, 1], BRACKET), 

172 TestInfo('("""Open string in bracket\n', [0, 1], FIRST), 

173 TestInfo('a = (1 + 2) - 5 *\\\n', [0, 1], BACKSLASH), # No bracket. 

174 TestInfo('\n def function1(self, a,\n b):\n', 

175 [0, 1, 3], NONE), 

176 TestInfo('\n def function1(self, a,\\\n', [0, 1, 2], BRACKET), 

177 TestInfo('\n def function1(self, a,\n', [0, 1, 2], BRACKET), 

178 TestInfo('())\n', [0, 1], NONE), # Extra closer. 

179 TestInfo(')(\n', [0, 1], BRACKET), # Extra closer. 

180 # For the mismatched example, it doesn't look like continuation. 

181 TestInfo('{)(]\n', [0, 1], NONE), # Mismatched. 

182 ) 

183 

184 for test in tests: 1e

185 with self.subTest(string=test.string): 1e

186 setcode(test.string) # resets study_level 1e

187 study() 1e

188 eq(p.study_level, 1) 1e

189 eq(p.goodlines, test.goodlines) 1e

190 eq(p.continuation, test.continuation) 1e

191 

192 # Called again, just returns without reprocessing. 

193 self.assertIsNone(study()) 1e

194 

195 def test_get_continuation_type(self): 

196 eq = self.assertEqual 1i

197 p = self.parser 1i

198 setcode = p.set_code 1i

199 gettype = p.get_continuation_type 1i

200 

201 (NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5) 1i

202 TestInfo = namedtuple('TestInfo', ['string', 'continuation']) 1i

203 tests = ( 1i

204 TestInfo('', NONE), 

205 TestInfo('"""This is a continuation docstring.\n', FIRST), 

206 TestInfo("'''This is a multiline-continued docstring.\n\n", NEXT), 

207 TestInfo('a = (1 + 2) - 5 *\\\n', BACKSLASH), 

208 TestInfo('\n def function1(self, a,\\\n', BRACKET) 

209 ) 

210 

211 for test in tests: 1i

212 with self.subTest(string=test.string): 1i

213 setcode(test.string) 1i

214 eq(gettype(), test.continuation) 1i

215 

216 def test_study2(self): 

217 eq = self.assertEqual 1c

218 p = self.parser 1c

219 setcode = p.set_code 1c

220 study = p._study2 1c

221 

222 TestInfo = namedtuple('TestInfo', ['string', 'start', 'end', 'lastch', 1c

223 'openbracket', 'bracketing']) 

224 tests = ( 1c

225 TestInfo('', 0, 0, '', None, ((0, 0),)), 

226 TestInfo("'''This is a multiline continuation docstring.\n\n", 

227 0, 48, "'", None, ((0, 0), (0, 1), (48, 0))), 

228 TestInfo(' # Comment\\\n', 

229 0, 12, '', None, ((0, 0), (1, 1), (12, 0))), 

230 # A comment without a space is a special case 

231 TestInfo(' #Comment\\\n', 

232 0, 0, '', None, ((0, 0),)), 

233 # Backslash continuation. 

234 TestInfo('a = (1 + 2) - 5 *\\\n', 

235 0, 19, '*', None, ((0, 0), (4, 1), (11, 0))), 

236 # Bracket continuation with close. 

237 TestInfo('\n def function1(self, a,\n b):\n', 

238 1, 48, ':', None, ((1, 0), (17, 1), (46, 0))), 

239 # Bracket continuation with unneeded backslash. 

240 TestInfo('\n def function1(self, a,\\\n', 

241 1, 28, ',', 17, ((1, 0), (17, 1))), 

242 # Bracket continuation. 

243 TestInfo('\n def function1(self, a,\n', 

244 1, 27, ',', 17, ((1, 0), (17, 1))), 

245 # Bracket continuation with comment at end of line with text. 

246 TestInfo('\n def function1(self, a, # End of line comment.\n', 

247 1, 51, ',', 17, ((1, 0), (17, 1), (28, 2), (51, 1))), 

248 # Multi-line statement with comment line in between code lines. 

249 TestInfo(' a = ["first item",\n # Comment line\n "next item",\n', 

250 0, 55, ',', 6, ((0, 0), (6, 1), (7, 2), (19, 1), 

251 (23, 2), (38, 1), (42, 2), (53, 1))), 

252 TestInfo('())\n', 

253 0, 4, ')', None, ((0, 0), (0, 1), (2, 0), (3, 0))), 

254 TestInfo(')(\n', 0, 3, '(', 1, ((0, 0), (1, 0), (1, 1))), 

255 # Wrong closers still decrement stack level. 

256 TestInfo('{)(]\n', 

257 0, 5, ']', None, ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))), 

258 # Character after backslash. 

259 TestInfo(':\\a\n', 0, 4, '\\a', None, ((0, 0),)), 

260 TestInfo('\n', 0, 0, '', None, ((0, 0),)), 

261 ) 

262 

263 for test in tests: 1c

264 with self.subTest(string=test.string): 1c

265 setcode(test.string) 1c

266 study() 1c

267 eq(p.study_level, 2) 1c

268 eq(p.stmt_start, test.start) 1c

269 eq(p.stmt_end, test.end) 1c

270 eq(p.lastch, test.lastch) 1c

271 eq(p.lastopenbracketpos, test.openbracket) 1c

272 eq(p.stmt_bracketing, test.bracketing) 1c

273 

274 # Called again, just returns without reprocessing. 

275 self.assertIsNone(study()) 1c

276 

277 def test_get_num_lines_in_stmt(self): 

278 eq = self.assertEqual 1f

279 p = self.parser 1f

280 setcode = p.set_code 1f

281 getlines = p.get_num_lines_in_stmt 1f

282 

283 TestInfo = namedtuple('TestInfo', ['string', 'lines']) 1f

284 tests = ( 1f

285 TestInfo('[x for x in a]\n', 1), # Closed on one line. 

286 TestInfo('[x\nfor x in a\n', 2), # Not closed. 

287 TestInfo('[x\\\nfor x in a\\\n', 2), # "", unneeded backslashes. 

288 TestInfo('[x\nfor x in a\n]\n', 3), # Closed on multi-line. 

289 TestInfo('\n"""Docstring comment L1"""\nL2\nL3\nL4\n', 1), 

290 TestInfo('\n"""Docstring comment L1\nL2"""\nL3\nL4\n', 1), 

291 TestInfo('\n"""Docstring comment L1\\\nL2\\\nL3\\\nL4\\\n', 4), 

292 TestInfo('\n\n"""Docstring comment L1\\\nL2\\\nL3\\\nL4\\\n"""\n', 5) 

293 ) 

294 

295 # Blank string doesn't have enough elements in goodlines. 

296 setcode('') 1f

297 with self.assertRaises(IndexError): 1f

298 getlines() 1f

299 

300 for test in tests: 1f

301 with self.subTest(string=test.string): 1f

302 setcode(test.string) 1f

303 eq(getlines(), test.lines) 1f

304 

305 def test_compute_bracket_indent(self): 

306 eq = self.assertEqual 1g

307 p = self.parser 1g

308 setcode = p.set_code 1g

309 indent = p.compute_bracket_indent 1g

310 

311 TestInfo = namedtuple('TestInfo', ['string', 'spaces']) 1g

312 tests = ( 1g

313 TestInfo('def function1(self, a,\n', 14), 

314 # Characters after bracket. 

315 TestInfo('\n def function1(self, a,\n', 18), 

316 TestInfo('\n\tdef function1(self, a,\n', 18), 

317 # No characters after bracket. 

318 TestInfo('\n def function1(\n', 8), 

319 TestInfo('\n\tdef function1(\n', 8), 

320 TestInfo('\n def function1( \n', 8), # Ignore extra spaces. 

321 TestInfo('[\n"first item",\n # Comment line\n "next item",\n', 0), 

322 TestInfo('[\n "first item",\n # Comment line\n "next item",\n', 2), 

323 TestInfo('["first item",\n # Comment line\n "next item",\n', 1), 

324 TestInfo('(\n', 4), 

325 TestInfo('(a\n', 1), 

326 ) 

327 

328 # Must be C_BRACKET continuation type. 

329 setcode('def function1(self, a, b):\n') 1g

330 with self.assertRaises(AssertionError): 1g

331 indent() 1g

332 

333 for test in tests: 1g

334 setcode(test.string) 1g

335 eq(indent(), test.spaces) 1g

336 

337 def test_compute_backslash_indent(self): 

338 eq = self.assertEqual 1d

339 p = self.parser 1d

340 setcode = p.set_code 1d

341 indent = p.compute_backslash_indent 1d

342 

343 # Must be C_BACKSLASH continuation type. 

344 errors = (('def function1(self, a, b\\\n'), # Bracket. 1d

345 (' """ (\\\n'), # Docstring. 

346 ('a = #\\\n'), # Inline comment. 

347 ) 

348 for string in errors: 1d

349 with self.subTest(string=string): 1d

350 setcode(string) 1d

351 with self.assertRaises(AssertionError): 1d

352 indent() 1d

353 

354 TestInfo = namedtuple('TestInfo', ('string', 'spaces')) 1d

355 tests = (TestInfo('a = (1 + 2) - 5 *\\\n', 4), 1d

356 TestInfo('a = 1 + 2 - 5 *\\\n', 4), 

357 TestInfo(' a = 1 + 2 - 5 *\\\n', 8), 

358 TestInfo(' a = "spam"\\\n', 6), 

359 TestInfo(' a = \\\n"a"\\\n', 4), 

360 TestInfo(' a = #\\\n"a"\\\n', 5), 

361 TestInfo('a == \\\n', 2), 

362 TestInfo('a != \\\n', 2), 

363 # Difference between containing = and those not. 

364 TestInfo('\\\n', 2), 

365 TestInfo(' \\\n', 6), 

366 TestInfo('\t\\\n', 6), 

367 TestInfo('a\\\n', 3), 

368 TestInfo('{}\\\n', 4), 

369 TestInfo('(1 + 2) - 5 *\\\n', 3), 

370 ) 

371 for test in tests: 1d

372 with self.subTest(string=test.string): 1d

373 setcode(test.string) 1d

374 eq(indent(), test.spaces) 1d

375 

376 def test_get_base_indent_string(self): 

377 eq = self.assertEqual 1m

378 p = self.parser 1m

379 setcode = p.set_code 1m

380 baseindent = p.get_base_indent_string 1m

381 

382 TestInfo = namedtuple('TestInfo', ['string', 'indent']) 1m

383 tests = (TestInfo('', ''), 1m

384 TestInfo('def a():\n', ''), 

385 TestInfo('\tdef a():\n', '\t'), 

386 TestInfo(' def a():\n', ' '), 

387 TestInfo(' def a(\n', ' '), 

388 TestInfo('\t\n def a(\n', ' '), 

389 TestInfo('\t\n # Comment.\n', ' '), 

390 ) 

391 

392 for test in tests: 1m

393 with self.subTest(string=test.string): 1m

394 setcode(test.string) 1m

395 eq(baseindent(), test.indent) 1m

396 

397 def test_is_block_opener(self): 

398 yes = self.assertTrue 1j

399 no = self.assertFalse 1j

400 p = self.parser 1j

401 setcode = p.set_code 1j

402 opener = p.is_block_opener 1j

403 

404 TestInfo = namedtuple('TestInfo', ['string', 'assert_']) 1j

405 tests = ( 1j

406 TestInfo('def a():\n', yes), 

407 TestInfo('\n def function1(self, a,\n b):\n', yes), 

408 TestInfo(':\n', yes), 

409 TestInfo('a:\n', yes), 

410 TestInfo('):\n', yes), 

411 TestInfo('(:\n', yes), 

412 TestInfo('":\n', no), 

413 TestInfo('\n def function1(self, a,\n', no), 

414 TestInfo('def function1(self, a):\n pass\n', no), 

415 TestInfo('# A comment:\n', no), 

416 TestInfo('"""A docstring:\n', no), 

417 TestInfo('"""A docstring:\n', no), 

418 ) 

419 

420 for test in tests: 1j

421 with self.subTest(string=test.string): 1j

422 setcode(test.string) 1j

423 test.assert_(opener()) 1j

424 

425 def test_is_block_closer(self): 

426 yes = self.assertTrue 1k

427 no = self.assertFalse 1k

428 p = self.parser 1k

429 setcode = p.set_code 1k

430 closer = p.is_block_closer 1k

431 

432 TestInfo = namedtuple('TestInfo', ['string', 'assert_']) 1k

433 tests = ( 1k

434 TestInfo('return\n', yes), 

435 TestInfo('\tbreak\n', yes), 

436 TestInfo(' continue\n', yes), 

437 TestInfo(' raise\n', yes), 

438 TestInfo('pass \n', yes), 

439 TestInfo('pass\t\n', yes), 

440 TestInfo('return #\n', yes), 

441 TestInfo('raised\n', no), 

442 TestInfo('returning\n', no), 

443 TestInfo('# return\n', no), 

444 TestInfo('"""break\n', no), 

445 TestInfo('"continue\n', no), 

446 TestInfo('def function1(self, a):\n pass\n', yes), 

447 ) 

448 

449 for test in tests: 1k

450 with self.subTest(string=test.string): 1k

451 setcode(test.string) 1k

452 test.assert_(closer()) 1k

453 

454 def test_get_last_stmt_bracketing(self): 

455 eq = self.assertEqual 1n

456 p = self.parser 1n

457 setcode = p.set_code 1n

458 bracketing = p.get_last_stmt_bracketing 1n

459 

460 TestInfo = namedtuple('TestInfo', ['string', 'bracket']) 1n

461 tests = ( 1n

462 TestInfo('', ((0, 0),)), 

463 TestInfo('a\n', ((0, 0),)), 

464 TestInfo('()()\n', ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))), 

465 TestInfo('(\n)()\n', ((0, 0), (0, 1), (3, 0), (3, 1), (5, 0))), 

466 TestInfo('()\n()\n', ((3, 0), (3, 1), (5, 0))), 

467 TestInfo('()(\n)\n', ((0, 0), (0, 1), (2, 0), (2, 1), (5, 0))), 

468 TestInfo('(())\n', ((0, 0), (0, 1), (1, 2), (3, 1), (4, 0))), 

469 TestInfo('(\n())\n', ((0, 0), (0, 1), (2, 2), (4, 1), (5, 0))), 

470 # Same as matched test. 

471 TestInfo('{)(]\n', ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))), 

472 TestInfo('(((())\n', 

473 ((0, 0), (0, 1), (1, 2), (2, 3), (3, 4), (5, 3), (6, 2))), 

474 ) 

475 

476 for test in tests: 1n

477 with self.subTest(string=test.string): 1n

478 setcode(test.string) 1n

479 eq(bracketing(), test.bracket) 1n

480 

481 

482if __name__ == '__main__': 482 ↛ 483line 482 didn't jump to line 483, because the condition on line 482 was never true

483 unittest.main(verbosity=2)