import re
import unittest
from bpython.line import (
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):
"""'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), (d["<"], d[">"], s[d["<"] : d[">"]])
else:
return (d["|"], s), None
def line_with_cursor(cursor_offset, line):
return line[:cursor_offset] + "|" + line[cursor_offset:]
def encode(cursor_offset, line, result):
"""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, end, value = result
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"), (1, 4, "bdc")))
self.assertEqual(decode("a|d"), ((1, "abdcd"), (1, 4, "bdc")))
self.assertEqual(decode("ad|"), ((5, "abdcd"), (1, 4, "bdc")))
def test_encode(self):
self.assertEqual(encode(3, "abdcd", (1, 4, "bdc")), "ad")
self.assertEqual(encode(1, "abdcd", (1, 4, "bdc")), "a|d")
self.assertEqual(encode(4, "abdcd", (1, 4, "bdc")), "ad")
self.assertEqual(encode(5, "abdcd", (1, 4, "bdc")), "ad|")
def test_assert_access(self):
def dumb_func(cursor_offset, line):
return (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("