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
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-11 13:22 -0700
1"Test pyparse, coverage 96%."
3from idlelib import pyparse
4import unittest
5from collections import namedtuple
8class ParseMapTest(unittest.TestCase):
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
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')
24class PyParseTest(unittest.TestCase):
26 @classmethod
27 def setUpClass(cls):
28 cls.parser = pyparse.Parser(indentwidth=4, tabwidth=4)
30 @classmethod
31 def tearDownClass(cls):
32 del cls.parser
34 def test_init(self):
35 self.assertEqual(self.parser.indentwidth, 4) 1q
36 self.assertEqual(self.parser.tabwidth, 4) 1q
38 def test_set_code(self):
39 eq = self.assertEqual 1h
40 p = self.parser 1h
41 setcode = p.set_code 1h
43 # Not empty and doesn't end with newline.
44 with self.assertRaises(AssertionError): 1h
45 setcode('a') 1h
47 tests = ('', 1h
48 'a\n')
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
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
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
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
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
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
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
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
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
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
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
133 # Previous character is not a newline.
134 with self.assertRaises(AssertionError): 1l
135 p.set_lo(5) 1l
137 # A value of 0 doesn't change self.code.
138 p.set_lo(0) 1l
139 self.assertEqual(p.code, code) 1l
141 # An index that is preceded by a newline.
142 p.set_lo(pos) 1l
143 self.assertEqual(p.code, code[pos:]) 1l
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
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 )
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
192 # Called again, just returns without reprocessing.
193 self.assertIsNone(study()) 1e
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
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 )
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
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
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 )
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
274 # Called again, just returns without reprocessing.
275 self.assertIsNone(study()) 1c
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
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 )
295 # Blank string doesn't have enough elements in goodlines.
296 setcode('') 1f
297 with self.assertRaises(IndexError): 1f
298 getlines() 1f
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
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
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 )
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
333 for test in tests: 1g
334 setcode(test.string) 1g
335 eq(indent(), test.spaces) 1g
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
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
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
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
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 )
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
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
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 )
420 for test in tests: 1j
421 with self.subTest(string=test.string): 1j
422 setcode(test.string) 1j
423 test.assert_(opener()) 1j
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
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 )
449 for test in tests: 1k
450 with self.subTest(string=test.string): 1k
451 setcode(test.string) 1k
452 test.assert_(closer()) 1k
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
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 )
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
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)