Skip to content

Commit 4c0ea6b

Browse files
improve function docstring lookup
1 parent b093ced commit 4c0ea6b

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

bpython/repl.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from bpython.paste import PasteHelper, PastePinnwand, PasteFailed
5252
from bpython.patch_linecache import filename_for_console_input
5353
from bpython.translations import _, ngettext
54+
from bpython import simpleeval
5455

5556

5657
class RuntimeTimer(object):
@@ -518,7 +519,15 @@ def get_args(self):
518519
return False
519520

520521
try:
521-
f = self.get_object(func)
522+
if inspection.is_eval_safe_name(func):
523+
f = self.get_object(func)
524+
else:
525+
try:
526+
fake_cursor = self.current_line.index(func) + len(func)
527+
f = simpleeval.evaluate_current_attribute(
528+
fake_cursor, self.current_line, self.interp.locals)
529+
except simpleeval.EvaluationError:
530+
return False
522531
except Exception:
523532
# another case of needing to catch every kind of error
524533
# since user code is run in the case of descriptors
@@ -624,8 +633,6 @@ def complete(self, tab=False):
624633
current_block='\n'.join(self.buffer + [self.current_line]),
625634
complete_magic_methods=self.config.complete_magic_methods,
626635
history=self.history)
627-
# TODO implement completer.shown_before_tab == False (filenames
628-
# shouldn't fill screen)
629636

630637
if len(matches) == 0:
631638
self.matches_iter.clear()

bpython/simpleeval.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import ast
99
from six import string_types
1010
from six.moves import builtins
11-
from numbers import Number
1211

1312
from bpython import line as line_properties
1413
from bpython._py3compat import py3
@@ -136,14 +135,16 @@ def find_attribute_with_name(node, name):
136135
return r
137136

138137

139-
def evaluate_current_expression(cursor_offset, line, namespace={}):
138+
def evaluate_current_expression(cursor_offset, line, namespace=None):
140139
"""
141-
Return evaluted expression to the right of the dot of current attribute.
140+
Return evaluated expression to the right of the dot of current attribute.
142141
143142
build asts from with increasing numbers of characters.
144143
Find the biggest valid ast.
145144
Once our attribute access is a subtree, stop
146145
"""
146+
if namespace is None:
147+
namespace = {}
147148

148149
# in case attribute is blank, e.g. foo.| -> foo.xxx|
149150
temp_line = line[:cursor_offset] + 'xxx' + line[cursor_offset:]
@@ -174,3 +175,17 @@ def parse_trees(cursor_offset, line):
174175
return simple_eval(largest_ast, namespace)
175176
except ValueError:
176177
raise EvaluationError("Could not safely evaluate")
178+
179+
180+
def evaluate_current_attribute(cursor_offset, line, namespace=None):
181+
# this function runs user code in case of custom descriptors,
182+
# so could fail in any way
183+
184+
obj = evaluate_current_expression(cursor_offset, line, namespace)
185+
attr = line_properties.current_expression_attribute(cursor_offset, line)
186+
if attr is None:
187+
raise EvaluationError("No attribute found to look up")
188+
try:
189+
return getattr(obj, attr.word)
190+
except AttributeError:
191+
raise EvaluationError("can't lookup attribute %s on %r" % (attr.word, obj))

0 commit comments

Comments
 (0)