123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- "Test pyparse, coverage 96%."
- from idlelib import pyparse
- import unittest
- from collections import namedtuple
- class ParseMapTest(unittest.TestCase):
- def test_parsemap(self):
- keepwhite = {ord(c): ord(c) for c in ' \t\n\r'}
- mapping = pyparse.ParseMap(keepwhite)
- self.assertEqual(mapping[ord('\t')], ord('\t'))
- self.assertEqual(mapping[ord('a')], ord('x'))
- self.assertEqual(mapping[1000], ord('x'))
- def test_trans(self):
- # trans is the production instance of ParseMap, used in _study1
- parser = pyparse.Parser(4, 4)
- self.assertEqual('\t a([{b}])b"c\'d\n'.translate(pyparse.trans),
- 'xxx(((x)))x"x\'x\n')
- class PyParseTest(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- cls.parser = pyparse.Parser(indentwidth=4, tabwidth=4)
- @classmethod
- def tearDownClass(cls):
- del cls.parser
- def test_init(self):
- self.assertEqual(self.parser.indentwidth, 4)
- self.assertEqual(self.parser.tabwidth, 4)
- def test_set_code(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- # Not empty and doesn't end with newline.
- with self.assertRaises(AssertionError):
- setcode('a')
- tests = ('',
- 'a\n')
- for string in tests:
- with self.subTest(string=string):
- setcode(string)
- eq(p.code, string)
- eq(p.study_level, 0)
- def test_find_good_parse_start(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- start = p.find_good_parse_start
- def char_in_string_false(index): return False
- # First line starts with 'def' and ends with ':', then 0 is the pos.
- setcode('def spam():\n')
- eq(start(char_in_string_false), 0)
- # First line begins with a keyword in the list and ends
- # with an open brace, then 0 is the pos. This is how
- # hyperparser calls this function as the newline is not added
- # in the editor, but rather on the call to setcode.
- setcode('class spam( ' + ' \n')
- eq(start(char_in_string_false), 0)
- # Split def across lines.
- setcode('"""This is a module docstring"""\n'
- 'class C:\n'
- ' def __init__(self, a,\n'
- ' b=True):\n'
- ' pass\n'
- )
- pos0, pos = 33, 42 # Start of 'class...', ' def' lines.
- # Passing no value or non-callable should fail (issue 32989).
- with self.assertRaises(TypeError):
- start()
- with self.assertRaises(TypeError):
- start(False)
- # Make text look like a string. This returns pos as the start
- # position, but it's set to None.
- self.assertIsNone(start(is_char_in_string=lambda index: True))
- # Make all text look like it's not in a string. This means that it
- # found a good start position.
- eq(start(char_in_string_false), pos)
- # If the beginning of the def line is not in a string, then it
- # returns that as the index.
- eq(start(is_char_in_string=lambda index: index > pos), pos)
- # If the beginning of the def line is in a string, then it
- # looks for a previous index.
- eq(start(is_char_in_string=lambda index: index >= pos), pos0)
- # If everything before the 'def' is in a string, then returns None.
- # The non-continuation def line returns 44 (see below).
- eq(start(is_char_in_string=lambda index: index < pos), None)
- # Code without extra line break in def line - mostly returns the same
- # values.
- setcode('"""This is a module docstring"""\n'
- 'class C:\n'
- ' def __init__(self, a, b=True):\n'
- ' pass\n'
- ) # Does not affect class, def positions.
- eq(start(char_in_string_false), pos)
- eq(start(is_char_in_string=lambda index: index > pos), pos)
- eq(start(is_char_in_string=lambda index: index >= pos), pos0)
- # When the def line isn't split, this returns which doesn't match the
- # split line test.
- eq(start(is_char_in_string=lambda index: index < pos), pos)
- def test_set_lo(self):
- code = (
- '"""This is a module docstring"""\n'
- 'class C:\n'
- ' def __init__(self, a,\n'
- ' b=True):\n'
- ' pass\n'
- )
- pos = 42
- p = self.parser
- p.set_code(code)
- # Previous character is not a newline.
- with self.assertRaises(AssertionError):
- p.set_lo(5)
- # A value of 0 doesn't change self.code.
- p.set_lo(0)
- self.assertEqual(p.code, code)
- # An index that is preceded by a newline.
- p.set_lo(pos)
- self.assertEqual(p.code, code[pos:])
- def test_study1(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- study = p._study1
- (NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5)
- TestInfo = namedtuple('TestInfo', ['string', 'goodlines',
- 'continuation'])
- tests = (
- TestInfo('', [0], NONE),
- # Docstrings.
- TestInfo('"""This is a complete docstring."""\n', [0, 1], NONE),
- TestInfo("'''This is a complete docstring.'''\n", [0, 1], NONE),
- TestInfo('"""This is a continued docstring.\n', [0, 1], FIRST),
- TestInfo("'''This is a continued docstring.\n", [0, 1], FIRST),
- TestInfo('"""Closing quote does not match."\n', [0, 1], FIRST),
- TestInfo('"""Bracket in docstring [\n', [0, 1], FIRST),
- TestInfo("'''Incomplete two line docstring.\n\n", [0, 2], NEXT),
- # Single-quoted strings.
- TestInfo('"This is a complete string."\n', [0, 1], NONE),
- TestInfo('"This is an incomplete string.\n', [0, 1], NONE),
- TestInfo("'This is more incomplete.\n\n", [0, 1, 2], NONE),
- # Comment (backslash does not continue comments).
- TestInfo('# Comment\\\n', [0, 1], NONE),
- # Brackets.
- TestInfo('("""Complete string in bracket"""\n', [0, 1], BRACKET),
- TestInfo('("""Open string in bracket\n', [0, 1], FIRST),
- TestInfo('a = (1 + 2) - 5 *\\\n', [0, 1], BACKSLASH), # No bracket.
- TestInfo('\n def function1(self, a,\n b):\n',
- [0, 1, 3], NONE),
- TestInfo('\n def function1(self, a,\\\n', [0, 1, 2], BRACKET),
- TestInfo('\n def function1(self, a,\n', [0, 1, 2], BRACKET),
- TestInfo('())\n', [0, 1], NONE), # Extra closer.
- TestInfo(')(\n', [0, 1], BRACKET), # Extra closer.
- # For the mismatched example, it doesn't look like continuation.
- TestInfo('{)(]\n', [0, 1], NONE), # Mismatched.
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string) # resets study_level
- study()
- eq(p.study_level, 1)
- eq(p.goodlines, test.goodlines)
- eq(p.continuation, test.continuation)
- # Called again, just returns without reprocessing.
- self.assertIsNone(study())
- def test_get_continuation_type(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- gettype = p.get_continuation_type
- (NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5)
- TestInfo = namedtuple('TestInfo', ['string', 'continuation'])
- tests = (
- TestInfo('', NONE),
- TestInfo('"""This is a continuation docstring.\n', FIRST),
- TestInfo("'''This is a multiline-continued docstring.\n\n", NEXT),
- TestInfo('a = (1 + 2) - 5 *\\\n', BACKSLASH),
- TestInfo('\n def function1(self, a,\\\n', BRACKET)
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- eq(gettype(), test.continuation)
- def test_study2(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- study = p._study2
- TestInfo = namedtuple('TestInfo', ['string', 'start', 'end', 'lastch',
- 'openbracket', 'bracketing'])
- tests = (
- TestInfo('', 0, 0, '', None, ((0, 0),)),
- TestInfo("'''This is a multiline continuation docstring.\n\n",
- 0, 48, "'", None, ((0, 0), (0, 1), (48, 0))),
- TestInfo(' # Comment\\\n',
- 0, 12, '', None, ((0, 0), (1, 1), (12, 0))),
- # A comment without a space is a special case
- TestInfo(' #Comment\\\n',
- 0, 0, '', None, ((0, 0),)),
- # Backslash continuation.
- TestInfo('a = (1 + 2) - 5 *\\\n',
- 0, 19, '*', None, ((0, 0), (4, 1), (11, 0))),
- # Bracket continuation with close.
- TestInfo('\n def function1(self, a,\n b):\n',
- 1, 48, ':', None, ((1, 0), (17, 1), (46, 0))),
- # Bracket continuation with unneeded backslash.
- TestInfo('\n def function1(self, a,\\\n',
- 1, 28, ',', 17, ((1, 0), (17, 1))),
- # Bracket continuation.
- TestInfo('\n def function1(self, a,\n',
- 1, 27, ',', 17, ((1, 0), (17, 1))),
- # Bracket continuation with comment at end of line with text.
- TestInfo('\n def function1(self, a, # End of line comment.\n',
- 1, 51, ',', 17, ((1, 0), (17, 1), (28, 2), (51, 1))),
- # Multi-line statement with comment line in between code lines.
- TestInfo(' a = ["first item",\n # Comment line\n "next item",\n',
- 0, 55, ',', 6, ((0, 0), (6, 1), (7, 2), (19, 1),
- (23, 2), (38, 1), (42, 2), (53, 1))),
- TestInfo('())\n',
- 0, 4, ')', None, ((0, 0), (0, 1), (2, 0), (3, 0))),
- TestInfo(')(\n', 0, 3, '(', 1, ((0, 0), (1, 0), (1, 1))),
- # Wrong closers still decrement stack level.
- TestInfo('{)(]\n',
- 0, 5, ']', None, ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))),
- # Character after backslash.
- TestInfo(':\\a\n', 0, 4, '\\a', None, ((0, 0),)),
- TestInfo('\n', 0, 0, '', None, ((0, 0),)),
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- study()
- eq(p.study_level, 2)
- eq(p.stmt_start, test.start)
- eq(p.stmt_end, test.end)
- eq(p.lastch, test.lastch)
- eq(p.lastopenbracketpos, test.openbracket)
- eq(p.stmt_bracketing, test.bracketing)
- # Called again, just returns without reprocessing.
- self.assertIsNone(study())
- def test_get_num_lines_in_stmt(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- getlines = p.get_num_lines_in_stmt
- TestInfo = namedtuple('TestInfo', ['string', 'lines'])
- tests = (
- TestInfo('[x for x in a]\n', 1), # Closed on one line.
- TestInfo('[x\nfor x in a\n', 2), # Not closed.
- TestInfo('[x\\\nfor x in a\\\n', 2), # "", unneeded backslashes.
- TestInfo('[x\nfor x in a\n]\n', 3), # Closed on multi-line.
- TestInfo('\n"""Docstring comment L1"""\nL2\nL3\nL4\n', 1),
- TestInfo('\n"""Docstring comment L1\nL2"""\nL3\nL4\n', 1),
- TestInfo('\n"""Docstring comment L1\\\nL2\\\nL3\\\nL4\\\n', 4),
- TestInfo('\n\n"""Docstring comment L1\\\nL2\\\nL3\\\nL4\\\n"""\n', 5)
- )
- # Blank string doesn't have enough elements in goodlines.
- setcode('')
- with self.assertRaises(IndexError):
- getlines()
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- eq(getlines(), test.lines)
- def test_compute_bracket_indent(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- indent = p.compute_bracket_indent
- TestInfo = namedtuple('TestInfo', ['string', 'spaces'])
- tests = (
- TestInfo('def function1(self, a,\n', 14),
- # Characters after bracket.
- TestInfo('\n def function1(self, a,\n', 18),
- TestInfo('\n\tdef function1(self, a,\n', 18),
- # No characters after bracket.
- TestInfo('\n def function1(\n', 8),
- TestInfo('\n\tdef function1(\n', 8),
- TestInfo('\n def function1( \n', 8), # Ignore extra spaces.
- TestInfo('[\n"first item",\n # Comment line\n "next item",\n', 0),
- TestInfo('[\n "first item",\n # Comment line\n "next item",\n', 2),
- TestInfo('["first item",\n # Comment line\n "next item",\n', 1),
- TestInfo('(\n', 4),
- TestInfo('(a\n', 1),
- )
- # Must be C_BRACKET continuation type.
- setcode('def function1(self, a, b):\n')
- with self.assertRaises(AssertionError):
- indent()
- for test in tests:
- setcode(test.string)
- eq(indent(), test.spaces)
- def test_compute_backslash_indent(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- indent = p.compute_backslash_indent
- # Must be C_BACKSLASH continuation type.
- errors = (('def function1(self, a, b\\\n'), # Bracket.
- (' """ (\\\n'), # Docstring.
- ('a = #\\\n'), # Inline comment.
- )
- for string in errors:
- with self.subTest(string=string):
- setcode(string)
- with self.assertRaises(AssertionError):
- indent()
- TestInfo = namedtuple('TestInfo', ('string', 'spaces'))
- tests = (TestInfo('a = (1 + 2) - 5 *\\\n', 4),
- TestInfo('a = 1 + 2 - 5 *\\\n', 4),
- TestInfo(' a = 1 + 2 - 5 *\\\n', 8),
- TestInfo(' a = "spam"\\\n', 6),
- TestInfo(' a = \\\n"a"\\\n', 4),
- TestInfo(' a = #\\\n"a"\\\n', 5),
- TestInfo('a == \\\n', 2),
- TestInfo('a != \\\n', 2),
- # Difference between containing = and those not.
- TestInfo('\\\n', 2),
- TestInfo(' \\\n', 6),
- TestInfo('\t\\\n', 6),
- TestInfo('a\\\n', 3),
- TestInfo('{}\\\n', 4),
- TestInfo('(1 + 2) - 5 *\\\n', 3),
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- eq(indent(), test.spaces)
- def test_get_base_indent_string(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- baseindent = p.get_base_indent_string
- TestInfo = namedtuple('TestInfo', ['string', 'indent'])
- tests = (TestInfo('', ''),
- TestInfo('def a():\n', ''),
- TestInfo('\tdef a():\n', '\t'),
- TestInfo(' def a():\n', ' '),
- TestInfo(' def a(\n', ' '),
- TestInfo('\t\n def a(\n', ' '),
- TestInfo('\t\n # Comment.\n', ' '),
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- eq(baseindent(), test.indent)
- def test_is_block_opener(self):
- yes = self.assertTrue
- no = self.assertFalse
- p = self.parser
- setcode = p.set_code
- opener = p.is_block_opener
- TestInfo = namedtuple('TestInfo', ['string', 'assert_'])
- tests = (
- TestInfo('def a():\n', yes),
- TestInfo('\n def function1(self, a,\n b):\n', yes),
- TestInfo(':\n', yes),
- TestInfo('a:\n', yes),
- TestInfo('):\n', yes),
- TestInfo('(:\n', yes),
- TestInfo('":\n', no),
- TestInfo('\n def function1(self, a,\n', no),
- TestInfo('def function1(self, a):\n pass\n', no),
- TestInfo('# A comment:\n', no),
- TestInfo('"""A docstring:\n', no),
- TestInfo('"""A docstring:\n', no),
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- test.assert_(opener())
- def test_is_block_closer(self):
- yes = self.assertTrue
- no = self.assertFalse
- p = self.parser
- setcode = p.set_code
- closer = p.is_block_closer
- TestInfo = namedtuple('TestInfo', ['string', 'assert_'])
- tests = (
- TestInfo('return\n', yes),
- TestInfo('\tbreak\n', yes),
- TestInfo(' continue\n', yes),
- TestInfo(' raise\n', yes),
- TestInfo('pass \n', yes),
- TestInfo('pass\t\n', yes),
- TestInfo('return #\n', yes),
- TestInfo('raised\n', no),
- TestInfo('returning\n', no),
- TestInfo('# return\n', no),
- TestInfo('"""break\n', no),
- TestInfo('"continue\n', no),
- TestInfo('def function1(self, a):\n pass\n', yes),
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- test.assert_(closer())
- def test_get_last_stmt_bracketing(self):
- eq = self.assertEqual
- p = self.parser
- setcode = p.set_code
- bracketing = p.get_last_stmt_bracketing
- TestInfo = namedtuple('TestInfo', ['string', 'bracket'])
- tests = (
- TestInfo('', ((0, 0),)),
- TestInfo('a\n', ((0, 0),)),
- TestInfo('()()\n', ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))),
- TestInfo('(\n)()\n', ((0, 0), (0, 1), (3, 0), (3, 1), (5, 0))),
- TestInfo('()\n()\n', ((3, 0), (3, 1), (5, 0))),
- TestInfo('()(\n)\n', ((0, 0), (0, 1), (2, 0), (2, 1), (5, 0))),
- TestInfo('(())\n', ((0, 0), (0, 1), (1, 2), (3, 1), (4, 0))),
- TestInfo('(\n())\n', ((0, 0), (0, 1), (2, 2), (4, 1), (5, 0))),
- # Same as matched test.
- TestInfo('{)(]\n', ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))),
- TestInfo('(((())\n',
- ((0, 0), (0, 1), (1, 2), (2, 3), (3, 4), (5, 3), (6, 2))),
- )
- for test in tests:
- with self.subTest(string=test.string):
- setcode(test.string)
- eq(bracketing(), test.bracket)
- if __name__ == '__main__':
- unittest.main(verbosity=2)
|