X Tutup
import re from typing import Optional, Tuple import unittest from bpython.line import ( LinePart, current_word, current_dict_key, current_dict, current_string, current_object, current_object_attribute, current_from_import_from, current_from_import_import, current_import, current_method_definition_name, current_single_word, current_expression_attribute, current_dotted_attribute, ) def cursor(s): """'ab|c' -> (2, 'abc')""" cursor_offset = s.index("|") line = s[:cursor_offset] + s[cursor_offset + 1 :] return cursor_offset, line def decode(s: str) -> tuple[tuple[int, str], LinePart | None]: """'ad' -> ((3, 'abcd'), (1, 3, 'bdc'))""" if not s.count("|") == 1: raise ValueError("match helper needs | to occur once") if s.count("<") != s.count(">") or s.count("<") not in (0, 1): raise ValueError("match helper needs <, and > to occur just once") matches = list(re.finditer(r"[<>|]", s)) assert len(matches) in [1, 3], [m.group() for m in matches] d = {} for i, m in enumerate(matches): d[m.group(0)] = m.start() - i s = s[: m.start() - i] + s[m.end() - i :] assert len(d) in [1, 3], "need all the parts just once! %r" % d if "<" in d: return (d["|"], s), LinePart(d["<"], d[">"], s[d["<"] : d[">"]]) else: return (d["|"], s), None def line_with_cursor(cursor_offset: int, line: str) -> str: return line[:cursor_offset] + "|" + line[cursor_offset:] def encode(cursor_offset: int, line: str, result: LinePart | None) -> str: """encode(3, 'abdcd', (1, 3, 'bdc')) -> ad' Written for prettier assert error messages """ encoded_line = line_with_cursor(cursor_offset, line) if result is None: return encoded_line start = result.start end = result.stop value = result.word assert line[start:end] == value if start < cursor_offset: encoded_line = encoded_line[:start] + "<" + encoded_line[start:] else: encoded_line = ( encoded_line[: start + 1] + "<" + encoded_line[start + 1 :] ) if end < cursor_offset: encoded_line = encoded_line[: end + 1] + ">" + encoded_line[end + 1 :] else: encoded_line = encoded_line[: end + 2] + ">" + encoded_line[end + 2 :] return encoded_line class LineTestCase(unittest.TestCase): def assertAccess(self, s): r"""Asserts that self.func matches as described by s, which uses a little language to describe matches: abcdhijklmnopqrstuvwx|yz /|\ /|\ /|\ | | | the function should the current cursor position match this "efg" is between the x and y """ (cursor_offset, line), match = decode(s) result = self.func(cursor_offset, line) self.assertEqual( result, match, "%s(%r) result\n%r (%r) doesn't match expected\n%r (%r)" % ( self.func.__name__, line_with_cursor(cursor_offset, line), encode(cursor_offset, line, result), result, s, match, ), ) class TestHelpers(LineTestCase): def test_I(self): self.assertEqual(cursor("asd|fgh"), (3, "asdfgh")) def test_decode(self): self.assertEqual( decode("ad"), ((3, "abdcd"), LinePart(1, 4, "bdc")) ) self.assertEqual( decode("a|d"), ((1, "abdcd"), LinePart(1, 4, "bdc")) ) self.assertEqual( decode("ad|"), ((5, "abdcd"), LinePart(1, 4, "bdc")) ) def test_encode(self): self.assertEqual(encode(3, "abdcd", LinePart(1, 4, "bdc")), "ad") self.assertEqual(encode(1, "abdcd", LinePart(1, 4, "bdc")), "a|d") self.assertEqual(encode(4, "abdcd", LinePart(1, 4, "bdc")), "ad") self.assertEqual(encode(5, "abdcd", LinePart(1, 4, "bdc")), "ad|") def test_assert_access(self): def dumb_func(cursor_offset, line): return LinePart(0, 2, "ab") self.func = dumb_func self.assertAccess("d") class TestCurrentWord(LineTestCase): def setUp(self): self.func = current_word def test_simple(self): self.assertAccess("|") self.assertAccess("|asdf") self.assertAccess("") self.assertAccess("") self.assertAccess("") self.assertAccess("asdf + ") self.assertAccess(" + asdf") def test_inside(self): self.assertAccess("") self.assertAccess("") def test_dots(self): self.assertAccess("") self.assertAccess("") self.assertAccess("") self.assertAccess("stuff[stuff] + {123: 456} + ") self.assertAccess("stuff[]") self.assertAccess("stuff[asdf[]") def test_non_dots(self): self.assertAccess("].asdf|") self.assertAccess(").asdf|") self.assertAccess("foo[0].asdf|") self.assertAccess("foo().asdf|") self.assertAccess("foo().|") self.assertAccess("foo().asdf.|") self.assertAccess("foo[0].asdf.|") def test_open_paren(self): self.assertAccess("") # documenting current behavior - TODO is this intended? class TestCurrentDictKey(LineTestCase): def setUp(self): self.func = current_dict_key def test_simple(self): self.assertAccess("asdf|") self.assertAccess("asdf|") self.assertAccess("asdf[<>|") self.assertAccess("asdf[<>|]") self.assertAccess("object.dict[") self.assertAccess("asdf|") self.assertAccess("asdf[<(>|]") self.assertAccess("asdf[<(1>|]") self.assertAccess("asdf[<(1,>|]") self.assertAccess("asdf[<(1,)>|]") self.assertAccess("asdf[<(1, >|]") self.assertAccess("asdf[<(1, 2)>|]") # TODO self.assertAccess('d[d[<12|>') self.assertAccess("d[<'a>|") self.assertAccess("object.dict['a'bcd'], object.dict[<'abc>|") self.assertAccess("object.dict[<'a'bcd'>|], object.dict['abc") self.assertAccess(r"object.dict[<'a\'\\\"\n\\'>|") self.assertAccess("object.dict[<\"abc'>|") self.assertAccess("object.dict[<(1, 'apple', 2.134>|]") self.assertAccess("object.dict[<(1, 'apple', 2.134)>|]") self.assertAccess("object.dict[<-1000>|") self.assertAccess("object.dict[<-0.23948>|") self.assertAccess("object.dict[<'\U0001ffff>|") self.assertAccess(r"object.dict[<'a\'\\\"\n\\'>|]") self.assertAccess(r"object.dict[<'a\'\\\"\n\\|[[]'>") self.assertAccess('object.dict[<"a]bc[|]">]') self.assertAccess("object.dict[<'abcd[]>|") class TestCurrentDict(LineTestCase): def setUp(self): self.func = current_dict def test_simple(self): self.assertAccess("asdf|") self.assertAccess("asdf|") self.assertAccess("[|") self.assertAccess("[|]") self.assertAccess("[abc|") self.assertAccess("asdf|") class TestCurrentString(LineTestCase): def setUp(self): self.func = current_string def test_closed(self): self.assertAccess('""') self.assertAccess('""') self.assertAccess('"<|asdf>"') self.assertAccess("''") self.assertAccess("'<|asdf>'") self.assertAccess("''''''") self.assertAccess('""""""') self.assertAccess('asdf.afd("a") + ""') def test_open(self): self.assertAccess('"') self.assertAccess('"') self.assertAccess('"<|asdf>') self.assertAccess("'") self.assertAccess("'<|asdf>") self.assertAccess("'''") self.assertAccess('"""') self.assertAccess('asdf.afd("a") + "') class TestCurrentObject(LineTestCase): def setUp(self): self.func = current_object def test_simple(self): self.assertAccess(".attr1|") self.assertAccess(".|") self.assertAccess("Object|") self.assertAccess("Object|.") self.assertAccess(".|") self.assertAccess(".attr2|") self.assertAccess(".att|r1.attr2") self.assertAccess("stuff[stuff] + {123: 456} + .attr2|") self.assertAccess("stuff[asd|fg]") self.assertAccess("stuff[asdf[asd|fg]") class TestCurrentAttribute(LineTestCase): def setUp(self): self.func = current_object_attribute def test_simple(self): self.assertAccess("Object.") self.assertAccess("Object.attr1.") self.assertAccess("Object..attr2") self.assertAccess("stuff[stuff] + {123: 456} + Object.attr1.") self.assertAccess("stuff[asd|fg]") self.assertAccess("stuff[asdf[asd|fg]") self.assertAccess("Object.attr1.<|attr2>") self.assertAccess("Object..attr2") class TestCurrentFromImportFrom(LineTestCase): def setUp(self): self.func = current_from_import_from def test_simple(self): self.assertAccess("from import path") self.assertAccess("from import path|") self.assertAccess("if True|: from sys import path") self.assertAccess("if True: |from sys import path") self.assertAccess("if True: from import p|ath") self.assertAccess("if True: from sys imp|ort path") self.assertAccess("if True: from sys import |path") self.assertAccess("if True: from sys import path.stu|ff") self.assertAccess("if True: from import sep|") self.assertAccess("from ") class TestCurrentFromImportImport(LineTestCase): def setUp(self): self.func = current_from_import_import def test_simple(self): self.assertAccess("from sys import ") self.assertAccess("from sys import ") self.assertAccess("from sys import |path") self.assertAccess("from sys| import path") self.assertAccess("from s|ys import path") self.assertAccess("from |sys import path") self.assertAccess("from xml.dom import ") # because syntax error self.assertAccess("from xml.dom import Node.as|d") class TestCurrentImport(LineTestCase): def setUp(self): self.func = current_import def test_simple(self): self.assertAccess("import ") self.assertAccess("import ") self.assertAccess("import |path") self.assertAccess("import path, ") self.assertAccess("import path another|") self.assertAccess("if True: import ") self.assertAccess("if True: import ") self.assertAccess("if True: import ") self.assertAccess("if True: import as something") class TestMethodDefinitionName(LineTestCase): def setUp(self): self.func = current_method_definition_name def test_simple(self): self.assertAccess("def ") self.assertAccess(" def bar(x, y)|:") self.assertAccess(" def (x, y)") class TestSingleWord(LineTestCase): def setUp(self): self.func = current_single_word def test_simple(self): self.assertAccess("foo.bar|") self.assertAccess(".foo|") self.assertAccess(" ") class TestCurrentExpressionAttribute(LineTestCase): def setUp(self): self.func = current_expression_attribute def test_simple(self): self.assertAccess("Object..") self.assertAccess("Object.<|attr1>.") self.assertAccess("Object.(|)") self.assertAccess("Object.another.(|)") self.assertAccess("asdf asdf asdf.(abc|)") def test_without_dot(self): self.assertAccess("Object|") self.assertAccess("Object|.") self.assertAccess("|Object.") def test_with_whitespace(self): self.assertAccess("Object. ") self.assertAccess("Object .") self.assertAccess("Object . ") self.assertAccess("Object .asdf attr|") self.assertAccess("Object . attr") self.assertAccess("Object. asdf attr|") self.assertAccess("Object. attr") self.assertAccess("Object . asdf attr|") self.assertAccess("Object . attr") def test_indexing(self): self.assertAccess("abc[def].") self.assertAccess("abc[def].<|ghi>") self.assertAccess("abc[def].") self.assertAccess("abc[def].gh |i") self.assertAccess("abc[def]|") def test_strings(self): self.assertAccess('"hey".') self.assertAccess('"hey"|') self.assertAccess('"hey"|.a') self.assertAccess('"hey".') self.assertAccess('"hey".asdf d|') self.assertAccess('"hey".<|>') class TestCurrentDottedAttribute(LineTestCase): def setUp(self): self.func = current_dotted_attribute def test_simple(self): self.assertAccess("|") self.assertAccess("(|") self.assertAccess("[|") self.assertAccess("m.body[0].value|") self.assertAccess("m.body[0].attr.value|") if __name__ == "__main__": unittest.main()
X Tutup