python (3.12.0)
1 "Test pyparse, coverage 96%."
2
3 from idlelib import pyparse
4 import unittest
5 from collections import namedtuple
6
7
8 class ESC[4;38;5;81mParseMapTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
9
10 def test_parsemap(self):
11 keepwhite = {ord(c): ord(c) for c in ' \t\n\r'}
12 mapping = pyparse.ParseMap(keepwhite)
13 self.assertEqual(mapping[ord('\t')], ord('\t'))
14 self.assertEqual(mapping[ord('a')], ord('x'))
15 self.assertEqual(mapping[1000], ord('x'))
16
17 def test_trans(self):
18 # trans is the production instance of ParseMap, used in _study1
19 parser = pyparse.Parser(4, 4)
20 self.assertEqual('\t a([{b}])b"c\'d\n'.translate(pyparse.trans),
21 'xxx(((x)))x"x\'x\n')
22
23
24 class ESC[4;38;5;81mPyParseTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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)
36 self.assertEqual(self.parser.tabwidth, 4)
37
38 def test_set_code(self):
39 eq = self.assertEqual
40 p = self.parser
41 setcode = p.set_code
42
43 # Not empty and doesn't end with newline.
44 with self.assertRaises(AssertionError):
45 setcode('a')
46
47 tests = ('',
48 'a\n')
49
50 for string in tests:
51 with self.subTest(string=string):
52 setcode(string)
53 eq(p.code, string)
54 eq(p.study_level, 0)
55
56 def test_find_good_parse_start(self):
57 eq = self.assertEqual
58 p = self.parser
59 setcode = p.set_code
60 start = p.find_good_parse_start
61 def char_in_string_false(index): return False
62
63 # First line starts with 'def' and ends with ':', then 0 is the pos.
64 setcode('def spam():\n')
65 eq(start(char_in_string_false), 0)
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')
72 eq(start(char_in_string_false), 0)
73
74 # Split def across lines.
75 setcode('"""This is a module docstring"""\n'
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.
82
83 # Passing no value or non-callable should fail (issue 32989).
84 with self.assertRaises(TypeError):
85 start()
86 with self.assertRaises(TypeError):
87 start(False)
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))
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)
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)
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)
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)
106
107 # Code without extra line break in def line - mostly returns the same
108 # values.
109 setcode('"""This is a module docstring"""\n'
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)
115 eq(start(is_char_in_string=lambda index: index > pos), pos)
116 eq(start(is_char_in_string=lambda index: index >= pos), pos0)
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)
120
121 def test_set_lo(self):
122 code = (
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
130 p = self.parser
131 p.set_code(code)
132
133 # Previous character is not a newline.
134 with self.assertRaises(AssertionError):
135 p.set_lo(5)
136
137 # A value of 0 doesn't change self.code.
138 p.set_lo(0)
139 self.assertEqual(p.code, code)
140
141 # An index that is preceded by a newline.
142 p.set_lo(pos)
143 self.assertEqual(p.code, code[pos:])
144
145 def test_study1(self):
146 eq = self.assertEqual
147 p = self.parser
148 setcode = p.set_code
149 study = p._study1
150
151 (NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5)
152 TestInfo = namedtuple('TestInfo', ['string', 'goodlines',
153 'continuation'])
154 tests = (
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:
185 with self.subTest(string=test.string):
186 setcode(test.string) # resets study_level
187 study()
188 eq(p.study_level, 1)
189 eq(p.goodlines, test.goodlines)
190 eq(p.continuation, test.continuation)
191
192 # Called again, just returns without reprocessing.
193 self.assertIsNone(study())
194
195 def test_get_continuation_type(self):
196 eq = self.assertEqual
197 p = self.parser
198 setcode = p.set_code
199 gettype = p.get_continuation_type
200
201 (NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5)
202 TestInfo = namedtuple('TestInfo', ['string', 'continuation'])
203 tests = (
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:
212 with self.subTest(string=test.string):
213 setcode(test.string)
214 eq(gettype(), test.continuation)
215
216 def test_study2(self):
217 eq = self.assertEqual
218 p = self.parser
219 setcode = p.set_code
220 study = p._study2
221
222 TestInfo = namedtuple('TestInfo', ['string', 'start', 'end', 'lastch',
223 'openbracket', 'bracketing'])
224 tests = (
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:
264 with self.subTest(string=test.string):
265 setcode(test.string)
266 study()
267 eq(p.study_level, 2)
268 eq(p.stmt_start, test.start)
269 eq(p.stmt_end, test.end)
270 eq(p.lastch, test.lastch)
271 eq(p.lastopenbracketpos, test.openbracket)
272 eq(p.stmt_bracketing, test.bracketing)
273
274 # Called again, just returns without reprocessing.
275 self.assertIsNone(study())
276
277 def test_get_num_lines_in_stmt(self):
278 eq = self.assertEqual
279 p = self.parser
280 setcode = p.set_code
281 getlines = p.get_num_lines_in_stmt
282
283 TestInfo = namedtuple('TestInfo', ['string', 'lines'])
284 tests = (
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('')
297 with self.assertRaises(IndexError):
298 getlines()
299
300 for test in tests:
301 with self.subTest(string=test.string):
302 setcode(test.string)
303 eq(getlines(), test.lines)
304
305 def test_compute_bracket_indent(self):
306 eq = self.assertEqual
307 p = self.parser
308 setcode = p.set_code
309 indent = p.compute_bracket_indent
310
311 TestInfo = namedtuple('TestInfo', ['string', 'spaces'])
312 tests = (
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')
330 with self.assertRaises(AssertionError):
331 indent()
332
333 for test in tests:
334 setcode(test.string)
335 eq(indent(), test.spaces)
336
337 def test_compute_backslash_indent(self):
338 eq = self.assertEqual
339 p = self.parser
340 setcode = p.set_code
341 indent = p.compute_backslash_indent
342
343 # Must be C_BACKSLASH continuation type.
344 errors = (('def function1(self, a, b\\\n'), # Bracket.
345 (' """ (\\\n'), # Docstring.
346 ('a = #\\\n'), # Inline comment.
347 )
348 for string in errors:
349 with self.subTest(string=string):
350 setcode(string)
351 with self.assertRaises(AssertionError):
352 indent()
353
354 TestInfo = namedtuple('TestInfo', ('string', 'spaces'))
355 tests = (TestInfo('a = (1 + 2) - 5 *\\\n', 4),
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:
372 with self.subTest(string=test.string):
373 setcode(test.string)
374 eq(indent(), test.spaces)
375
376 def test_get_base_indent_string(self):
377 eq = self.assertEqual
378 p = self.parser
379 setcode = p.set_code
380 baseindent = p.get_base_indent_string
381
382 TestInfo = namedtuple('TestInfo', ['string', 'indent'])
383 tests = (TestInfo('', ''),
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:
393 with self.subTest(string=test.string):
394 setcode(test.string)
395 eq(baseindent(), test.indent)
396
397 def test_is_block_opener(self):
398 yes = self.assertTrue
399 no = self.assertFalse
400 p = self.parser
401 setcode = p.set_code
402 opener = p.is_block_opener
403
404 TestInfo = namedtuple('TestInfo', ['string', 'assert_'])
405 tests = (
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:
421 with self.subTest(string=test.string):
422 setcode(test.string)
423 test.assert_(opener())
424
425 def test_is_block_closer(self):
426 yes = self.assertTrue
427 no = self.assertFalse
428 p = self.parser
429 setcode = p.set_code
430 closer = p.is_block_closer
431
432 TestInfo = namedtuple('TestInfo', ['string', 'assert_'])
433 tests = (
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:
450 with self.subTest(string=test.string):
451 setcode(test.string)
452 test.assert_(closer())
453
454 def test_get_last_stmt_bracketing(self):
455 eq = self.assertEqual
456 p = self.parser
457 setcode = p.set_code
458 bracketing = p.get_last_stmt_bracketing
459
460 TestInfo = namedtuple('TestInfo', ['string', 'bracket'])
461 tests = (
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:
477 with self.subTest(string=test.string):
478 setcode(test.string)
479 eq(bracketing(), test.bracket)
480
481
482 if __name__ == '__main__':
483 unittest.main(verbosity=2)