Skip to content

Commit 0d78851

Browse files
go to global completion if dict finds no matches
also factored out safe_eval, and going to try to catch all the possible errors in there instead of the big try!
1 parent f1edce9 commit 0d78851

File tree

1 file changed

+31
-39
lines changed

1 file changed

+31
-39
lines changed

bpython/autocomplete.py

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -58,34 +58,34 @@ def attr_complete(text, namespace=None, config=None):
5858
else:
5959
autocomplete_mode = SUBSTRING
6060

61-
dictpattern = re.compile('[^\[\]]+\[$')
62-
def complete_dict(text):
63-
lastbracket_index = text.rindex('[')
64-
dexpr = text[:lastbracket_index].lstrip()
65-
obj = eval(dexpr, namespace)
66-
if obj and isinstance(obj, type({})) and obj.keys():
67-
return [dexpr + "[{!r}]".format(k) for k in obj.keys()]
68-
else:
69-
# empty dictionary
70-
return []
71-
7261
if "." in text:
73-
if dictpattern.match(text):
74-
return complete_dict(text)
75-
else:
76-
# Examples: 'foo.b' or 'foo[bar.'
77-
for i in range(1, len(text) + 1):
78-
if text[-i] == '[':
79-
i -= 1
80-
break
81-
methodtext = text[-i:]
82-
return [''.join([text[:-i], m]) for m in
83-
attr_matches(methodtext, namespace, autocomplete_mode)]
84-
elif dictpattern.match(text):
85-
return complete_dict(text)
62+
# Examples: 'foo.b' or 'foo[bar.'
63+
for i in range(1, len(text) + 1):
64+
if text[-i] == '[':
65+
i -= 1
66+
break
67+
methodtext = text[-i:]
68+
return [''.join([text[:-i], m]) for m in
69+
attr_matches(methodtext, namespace, autocomplete_mode)]
8670
else:
8771
return global_matches(text, namespace, autocomplete_mode)
8872

73+
class SafeEvalFailed(Exception):
74+
"""If this object is returned, safe_eval failed"""
75+
# Because every normal Python value is a possible return value of safe_eval
76+
77+
def safe_eval(expr, namespace):
78+
"""Not all that safe, just catches some errors"""
79+
if expr.isdigit():
80+
# Special case: float literal, using attrs here will result in
81+
# a SyntaxError
82+
return SafeEvalFailed
83+
try:
84+
obj = eval(expr, namespace)
85+
return obj
86+
except (NameError,):
87+
return SafeEvalFailed
88+
8989
def attr_matches(text, namespace, autocomplete_mode):
9090
"""Taken from rlcompleter.py and bent to my will.
9191
"""
@@ -98,11 +98,9 @@ def attr_matches(text, namespace, autocomplete_mode):
9898
return []
9999

100100
expr, attr = m.group(1, 3)
101-
if expr.isdigit():
102-
# Special case: float literal, using attrs here will result in
103-
# a SyntaxError
101+
obj = safe_eval(expr, namespace)
102+
if obj is SafeEvalFailed:
104103
return []
105-
obj = eval(expr, namespace)
106104
with inspection.AttrCleaner(obj):
107105
matches = attr_lookup(obj, expr, attr, autocomplete_mode)
108106
return matches
@@ -211,7 +209,7 @@ def get_completer(cursor_offset, current_line, locals_, argspec, config, magic_m
211209
return sorted(set(matches)), FilenameCompletion
212210

213211
matches = DictKeyCompletion.matches(cursor_offset, current_line, locals_=locals_, config=config)
214-
if matches is not None:
212+
if matches:
215213
return sorted(set(matches)), DictKeyCompletion
216214

217215
matches = AttrCompletion.matches(cursor_offset, current_line, locals_=locals_, config=config)
@@ -289,15 +287,7 @@ def matches(cls, cursor_offset, line, locals_, config):
289287
if r is None:
290288
return None
291289
cw = r[2]
292-
try:
293-
return attr_complete(cw, namespace=locals_, config=config)
294-
except Exception:
295-
# This sucks, but it's either that or list all the exceptions that could
296-
# possibly be raised here, so if anyone wants to do that, feel free to send me
297-
# a patch. XXX: Make sure you raise here if you're debugging the completion
298-
# stuff !
299-
pass
300-
return None
290+
return attr_complete(cw, namespace=locals_, config=config)
301291
locate = staticmethod(lineparts.current_word)
302292
format = staticmethod(after_last_dot)
303293

@@ -310,7 +300,9 @@ def matches(cls, cursor_offset, line, locals_, config):
310300
return None
311301
start, end, orig = r
312302
_, _, dexpr = lineparts.current_dict(cursor_offset, line)
313-
obj = eval(dexpr, locals_)
303+
obj = safe_eval(dexpr, locals_)
304+
if obj is SafeEvalFailed:
305+
return []
314306
if obj and isinstance(obj, type({})) and obj.keys():
315307
return ["{!r}]".format(k) for k in obj.keys() if repr(k).startswith(orig)]
316308
else:

0 commit comments

Comments
 (0)