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], Optional[LinePart]]:
"""'ad' -> ((3, 'abcd'), (1, 3, 'bdc'))"""
if not s.count("|") == 1:
raise ValueError("match helper needs | to occur once")
if s.count("") or s.count(" 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[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[|]")
self.assertAccess("asdf[|]")
self.assertAccess("asdf[|]")
self.assertAccess("asdf[|]")
self.assertAccess("asdf[|]")
# TODO self.assertAccess('d[d[')
self.assertAccess("d[|], object.dict['abc")
self.assertAccess(r"object.dict[|")
self.assertAccess(r"object.dict[|")
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('""')
self.assertAccess("''")
self.assertAccess("''")
self.assertAccess("''''''")
self.assertAccess('""""""')
self.assertAccess('asdf.afd("a") + ""')
def test_open(self):
self.assertAccess('"')
self.assertAccess('"')
self.assertAccess('"')
self.assertAccess("'")
self.assertAccess("'")
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.")
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..")
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].")
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()