Skip to content

Commit 95539cc

Browse files
committed
Avoid indexing and tuple unpacking of LinePart instances
1 parent 189da3e commit 95539cc

File tree

7 files changed

+64
-47
lines changed

7 files changed

+64
-47
lines changed

bpython/autocomplete.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def matches(
481481
if current_dict_parts is None:
482482
return None
483483

484-
_, _, dexpr = current_dict_parts
484+
dexpr = current_dict_parts.word
485485
try:
486486
obj = safe_eval(dexpr, locals_)
487487
except EvaluationError:

bpython/importcompletion.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,22 +135,22 @@ def complete(self, cursor_offset: int, line: str) -> Optional[Set[str]]:
135135
if import_import is not None:
136136
# `from a import <b|>` completion
137137
matches = self.module_matches(
138-
import_import[2], from_import_from[2]
138+
import_import.word, from_import_from.word
139139
)
140140
matches.update(
141-
self.attr_matches(import_import[2], from_import_from[2])
141+
self.attr_matches(import_import.word, from_import_from.word)
142142
)
143143
else:
144144
# `from <a|>` completion
145-
matches = self.module_attr_matches(from_import_from[2])
146-
matches.update(self.module_matches(from_import_from[2]))
145+
matches = self.module_attr_matches(from_import_from.word)
146+
matches.update(self.module_matches(from_import_from.word))
147147
return matches
148148

149149
cur_import = current_import(cursor_offset, line)
150150
if cur_import is not None:
151151
# `import <a|>` completion
152-
matches = self.module_matches(cur_import[2])
153-
matches.update(self.module_attr_matches(cur_import[2]))
152+
matches = self.module_matches(cur_import.word)
153+
matches.update(self.module_attr_matches(cur_import.word))
154154
return matches
155155
else:
156156
return None

bpython/line.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,14 @@ def current_object(cursor_offset: int, line: str) -> Optional[LinePart]:
130130
match = current_word(cursor_offset, line)
131131
if match is None:
132132
return None
133-
start, end, word = match
134133
s = ".".join(
135134
m.group(1)
136-
for m in _current_object_re.finditer(word)
137-
if m.end(1) + start < cursor_offset
135+
for m in _current_object_re.finditer(match.word)
136+
if m.end(1) + match.start < cursor_offset
138137
)
139138
if not s:
140139
return None
141-
return LinePart(start, start + len(s), s)
140+
return LinePart(match.start, match.start + len(s), s)
142141

143142

144143
_current_object_attribute_re = LazyReCompile(r"([\w_][\w0-9_]*)[.]?")
@@ -152,12 +151,13 @@ def current_object_attribute(
152151
match = current_word(cursor_offset, line)
153152
if match is None:
154153
return None
155-
start, end, word = match
156-
matches = _current_object_attribute_re.finditer(word)
154+
matches = _current_object_attribute_re.finditer(match.word)
157155
next(matches)
158156
for m in matches:
159-
if m.start(1) + start <= cursor_offset <= m.end(1) + start:
160-
return LinePart(m.start(1) + start, m.end(1) + start, m.group(1))
157+
if m.start(1) + match.start <= cursor_offset <= m.end(1) + match.start:
158+
return LinePart(
159+
m.start(1) + match.start, m.end(1) + match.start, m.group(1)
160+
)
161161
return None
162162

163163

@@ -266,11 +266,8 @@ def current_dotted_attribute(
266266
) -> Optional[LinePart]:
267267
"""The dotted attribute-object pair before the cursor"""
268268
match = current_word(cursor_offset, line)
269-
if match is None:
270-
return None
271-
start, end, word = match
272-
if "." in word[1:]:
273-
return LinePart(start, end, word)
269+
if match is not None and "." in match.word[1:]:
270+
return match
274271
return None
275272

276273

bpython/repl.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ def __init__(self) -> None:
239239
self.orig_line = ""
240240
# class describing the current type of completion
241241
self.completer: Optional[autocomplete.BaseCompletionType] = None
242+
self.start: Optional[int] = None
243+
self.end: Optional[int] = None
242244

243245
def __nonzero__(self) -> bool:
244246
"""MatchesIterator is False when word hasn't been replaced yet"""
@@ -277,16 +279,15 @@ def cur_line(self) -> Tuple[int, str]:
277279
made"""
278280
return self.substitute(self.current())
279281

280-
def substitute(self, match) -> Tuple[int, str]:
282+
def substitute(self, match: str) -> Tuple[int, str]:
281283
"""Returns a cursor offset and line with match substituted in"""
282284
assert self.completer is not None
283285

284-
start, end, _ = self.completer.locate(
285-
self.orig_cursor_offset, self.orig_line
286-
) # type: ignore
286+
lp = self.completer.locate(self.orig_cursor_offset, self.orig_line)
287+
assert lp is not None
287288
return (
288-
start + len(match),
289-
self.orig_line[:start] + match + self.orig_line[end:],
289+
lp.start + len(match),
290+
self.orig_line[: lp.start] + match + self.orig_line[lp.stop :],
290291
)
291292

292293
def is_cseq(self) -> bool:
@@ -331,9 +332,11 @@ def update(
331332
self.matches = matches
332333
self.completer = completer
333334
self.index = -1
334-
self.start, self.end, self.current_word = self.completer.locate(
335-
self.orig_cursor_offset, self.orig_line
336-
) # type: ignore
335+
lp = self.completer.locate(self.orig_cursor_offset, self.orig_line)
336+
assert lp is not None
337+
self.start = lp.start
338+
self.end = lp.stop
339+
self.current_word = lp.word
337340

338341
def clear(self) -> None:
339342
self.matches = []

bpython/test/test_autocomplete.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
has_jedi = False
1313

1414
from bpython import autocomplete
15+
from bpython.line import LinePart
1516

1617
glob_function = "glob.iglob"
1718

@@ -114,7 +115,9 @@ def test_locate_fails_when_not_in_string(self):
114115
self.assertEqual(self.completer.locate(4, "abcd"), None)
115116

116117
def test_locate_succeeds_when_in_string(self):
117-
self.assertEqual(self.completer.locate(4, "a'bc'd"), (2, 4, "bc"))
118+
self.assertEqual(
119+
self.completer.locate(4, "a'bc'd"), LinePart(2, 4, "bc")
120+
)
118121

119122
def test_issue_491(self):
120123
self.assertNotEqual(self.completer.matches(9, '"a[a.l-1]'), None)

bpython/test/test_line_properties.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import re
2+
from typing import Optional, Tuple
23
import unittest
34

45
from bpython.line import (
6+
LinePart,
57
current_word,
68
current_dict_key,
79
current_dict,
@@ -25,7 +27,7 @@ def cursor(s):
2527
return cursor_offset, line
2628

2729

28-
def decode(s):
30+
def decode(s: str) -> Tuple[Tuple[int, str], Optional[LinePart]]:
2931
"""'a<bd|c>d' -> ((3, 'abcd'), (1, 3, 'bdc'))"""
3032

3133
if not s.count("|") == 1:
@@ -41,24 +43,26 @@ def decode(s):
4143
assert len(d) in [1, 3], "need all the parts just once! %r" % d
4244

4345
if "<" in d:
44-
return (d["|"], s), (d["<"], d[">"], s[d["<"] : d[">"]])
46+
return (d["|"], s), LinePart(d["<"], d[">"], s[d["<"] : d[">"]])
4547
else:
4648
return (d["|"], s), None
4749

4850

49-
def line_with_cursor(cursor_offset, line):
51+
def line_with_cursor(cursor_offset: int, line: str) -> str:
5052
return line[:cursor_offset] + "|" + line[cursor_offset:]
5153

5254

53-
def encode(cursor_offset, line, result):
55+
def encode(cursor_offset: int, line: str, result: Optional[LinePart]) -> str:
5456
"""encode(3, 'abdcd', (1, 3, 'bdc')) -> a<bd|c>d'
5557
5658
Written for prettier assert error messages
5759
"""
5860
encoded_line = line_with_cursor(cursor_offset, line)
5961
if result is None:
6062
return encoded_line
61-
start, end, value = result
63+
start = result.start
64+
end = result.stop
65+
value = result.word
6266
assert line[start:end] == value
6367
if start < cursor_offset:
6468
encoded_line = encoded_line[:start] + "<" + encoded_line[start:]
@@ -107,19 +111,25 @@ def test_I(self):
107111
self.assertEqual(cursor("asd|fgh"), (3, "asdfgh"))
108112

109113
def test_decode(self):
110-
self.assertEqual(decode("a<bd|c>d"), ((3, "abdcd"), (1, 4, "bdc")))
111-
self.assertEqual(decode("a|<bdc>d"), ((1, "abdcd"), (1, 4, "bdc")))
112-
self.assertEqual(decode("a<bdc>d|"), ((5, "abdcd"), (1, 4, "bdc")))
114+
self.assertEqual(
115+
decode("a<bd|c>d"), ((3, "abdcd"), LinePart(1, 4, "bdc"))
116+
)
117+
self.assertEqual(
118+
decode("a|<bdc>d"), ((1, "abdcd"), LinePart(1, 4, "bdc"))
119+
)
120+
self.assertEqual(
121+
decode("a<bdc>d|"), ((5, "abdcd"), LinePart(1, 4, "bdc"))
122+
)
113123

114124
def test_encode(self):
115-
self.assertEqual(encode(3, "abdcd", (1, 4, "bdc")), "a<bd|c>d")
116-
self.assertEqual(encode(1, "abdcd", (1, 4, "bdc")), "a|<bdc>d")
117-
self.assertEqual(encode(4, "abdcd", (1, 4, "bdc")), "a<bdc|>d")
118-
self.assertEqual(encode(5, "abdcd", (1, 4, "bdc")), "a<bdc>d|")
125+
self.assertEqual(encode(3, "abdcd", LinePart(1, 4, "bdc")), "a<bd|c>d")
126+
self.assertEqual(encode(1, "abdcd", LinePart(1, 4, "bdc")), "a|<bdc>d")
127+
self.assertEqual(encode(4, "abdcd", LinePart(1, 4, "bdc")), "a<bdc|>d")
128+
self.assertEqual(encode(5, "abdcd", LinePart(1, 4, "bdc")), "a<bdc>d|")
119129

120130
def test_assert_access(self):
121131
def dumb_func(cursor_offset, line):
122-
return (0, 2, "ab")
132+
return LinePart(0, 2, "ab")
123133

124134
self.func = dumb_func
125135
self.assertAccess("<a|b>d")

bpython/test/test_repl.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
from unittest import mock
1111

1212
from bpython import config, repl, cli, autocomplete
13-
from bpython.test import MagicIterMock, FixLanguageTestCase as TestCase
14-
from bpython.test import TEST_CONFIG
13+
from bpython.line import LinePart
14+
from bpython.test import (
15+
MagicIterMock,
16+
FixLanguageTestCase as TestCase,
17+
TEST_CONFIG,
18+
)
1519

1620

1721
pypy = "PyPy" in sys.version
@@ -99,7 +103,7 @@ def test_update(self):
99103

100104
newmatches = ["string", "str", "set"]
101105
completer = mock.Mock()
102-
completer.locate.return_value = (0, 1, "s")
106+
completer.locate.return_value = LinePart(0, 1, "s")
103107
self.matches_iterator.update(1, "s", newmatches, completer)
104108

105109
newslice = islice(newmatches, 0, 3)
@@ -108,7 +112,7 @@ def test_update(self):
108112

109113
def test_cur_line(self):
110114
completer = mock.Mock()
111-
completer.locate.return_value = (
115+
completer.locate.return_value = LinePart(
112116
0,
113117
self.matches_iterator.orig_cursor_offset,
114118
self.matches_iterator.orig_line,

0 commit comments

Comments
 (0)