Skip to content

Commit 8254e87

Browse files
Test edge case for current word attribute
1 parent 504ddd3 commit 8254e87

File tree

3 files changed

+68
-37
lines changed

3 files changed

+68
-37
lines changed

bpython/line.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
All functions take cursor offset from the beginning of the line and the line of
44
Python code, and return None, or a tuple of the start index, end index, and the
55
word."""
6+
from __future__ import unicode_literals
7+
8+
import re
69

710
from itertools import chain
811
from collections import namedtuple
12+
from pygments.token import Token
13+
914
from bpython.lazyre import LazyReCompile
15+
from bpython._py3compat import PythonLexer, py3
1016

1117
current_word_re = LazyReCompile(r'[\w_][\w0-9._]*[(]?')
1218
LinePart = namedtuple('LinePart', ['start', 'stop', 'word'])
@@ -93,7 +99,7 @@ def current_object(cursor_offset, line):
9399
return LinePart(start, start+len(s), s)
94100

95101

96-
current_object_attribute_re = LazyReCompile(r'([\w_][\w0-9_]*)[.]?')
102+
current_object_attribute_re = LazyReCompile(r'([\w_][\w0-9_]*)')
97103

98104

99105
def current_object_attribute(cursor_offset, line):
@@ -264,14 +270,40 @@ def current_indexed_member_access_member(cursor_offset, line):
264270
if m.start(3) <= cursor_offset and m.end(3) >= cursor_offset:
265271
return LinePart(m.start(3), m.end(3), m.group(3))
266272

273+
current_simple_expression_re = LazyReCompile(
274+
r'''([a-zA-Z_][\w.]*)\[([a-zA-Z0-9_"']+)\]\.([\w.]*)''')
275+
276+
def _current_simple_expression(cursor_offset, line):
277+
"""
278+
Returns the current "simple expression" being attribute accessed
279+
280+
build asts from with increasing numbers of characters.
281+
Find the biggest valid ast.
282+
Once our attribute access is a subtree, stop
283+
284+
285+
"""
286+
for i in range(cursor):
287+
pass
288+
289+
267290
def current_simple_expression(cursor_offset, line):
268291
"""The expression attribute lookup being performed on
269292
270293
e.g. <foo[0][1].bar>.ba|z
271294
A "simple expression" contains only . lookup and [] indexing."""
272295

296+
273297
def current_simple_expression_attribute(cursor_offset, line):
274298
"""The attribute being looked up on a simple expression
275299
276300
e.g. foo[0][1].bar.<ba|z>
277301
A "simple expression" contains only . lookup and [] indexing."""
302+
303+
304+
305+
306+
307+
308+
309+

bpython/simpleeval.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,33 @@ def safe_getitem(obj, index):
8181
if type(obj) in (list, tuple, dict, bytes) + string_types:
8282
return obj[index]
8383
raise ValueError('unsafe to lookup on object of type %s' % (type(obj), ))
84+
85+
86+
class AttributeSearcher(NodeVisitor):
87+
"""Search for a Load of an Attribute at col_offset"""
88+
def visit_attribute(self, node):
89+
print node.attribute
90+
91+
def _current_simple_expression(cursor_offset, line):
92+
"""
93+
Returns the current "simple expression" being attribute accessed
94+
95+
build asts from with increasing numbers of characters.
96+
Find the biggest valid ast.
97+
Once our attribute access is a subtree, stop
98+
99+
100+
"""
101+
102+
# in case attribute is blank, e.g. foo.| -> foo.xxx|
103+
temp_line = line[:cursor_offset] + 'xxx' + line[cursor_offset:]
104+
temp_cursor = cursor_offset + 3
105+
106+
for i in range(temp_cursor-1, -1, -1):
107+
try:
108+
tree = parse(temp_line[i:temp_cursor])
109+
except SyntaxError:
110+
return None
111+
112+
113+

bpython/test/test_line_properties.py

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ def test_simple(self):
225225
self.assertAccess('stuff[asdf[asd|fg]')
226226
self.assertAccess('Object.attr1.<|attr2>')
227227
self.assertAccess('Object.<attr1|>.attr2')
228+
self.assertAccess('Object.<attr1|>.attr2')
229+
230+
def test_after_dot(self):
231+
self.assertAccess('Object.<attr1|>.')
232+
self.assertAccess('Object.attr1.|')
228233

229234

230235
class TestCurrentFromImportFrom(LineTestCase):
@@ -343,42 +348,6 @@ def test_simple(self):
343348
self.assertAccess('abc[def].gh |i')
344349
self.assertAccess('abc[def]|')
345350

346-
@unittest.skip("TODO")
347-
class TestCurrentSimpleExpression(LineTestCase):
348-
def setUp(self):
349-
self.func = current_simple_expression
350-
351-
def test_only_dots(self):
352-
self.assertAccess('<Object>.attr1|')
353-
self.assertAccess('<Object>.|')
354-
self.assertAccess('Object|')
355-
self.assertAccess('Object|.')
356-
self.assertAccess('<Object>.|')
357-
self.assertAccess('<Object.attr1>.attr2|')
358-
self.assertAccess('<Object>.att|r1.attr2')
359-
self.assertAccess('stuff[stuff] + {123: 456} + <Object.attr1>.attr2|')
360-
self.assertAccess('stuff[asd|fg]')
361-
self.assertAccess('stuff[asdf[asd|fg]')
362-
363-
def test_with_brackets(self):
364-
self.assertAccess('<foo[a]>.ba|r')
365-
self.assertAccess('<foo[a]>.ba|r baz[qux]xyzzy')
366-
self.assertAccess('foo[<bar[baz]>.qux|].xyzzy')
367-
self.assertAccess('<foo[bar[baz].qux]>.xyzzy|')
368-
self.assertAccess('foo[bar[baz].qux].xyzzy, <a>.b|')
369-
self.assertAccess('foo[bar[<baz>.|')
370-
self.assertAccess('foo[bar[<baz>.|] + 1].qux')
371-
372-
def test_cases_disallowed_by_simple_eval(self):
373-
# These are allowed for now, but could be changed.
374-
# for example, function calls are not allowed in simple expressions but
375-
# seem like they'd be a pain to weed out so we catch them in the next step."""
376-
self.assertAccess('foo().bar|')
377-
self.assertAccess('foo[bar(a, b)].baz|')
378-
self.assertAccess('foo(a, b).bar|')
379-
self.assertAccess('<(1 + 1)>.bar|')
380-
self.assertAccess('<(1 + 1 - foo.bar()[1])>.baz|')
381-
382351

383352
if __name__ == '__main__':
384353
unittest.main()

0 commit comments

Comments
 (0)