@ -100,52 +100,53 @@ class CallTips:
return rpcclt . remotecall ( " exec " , " get_the_calltip " ,
( expression , ) , { } )
else :
entity = self . get_entity ( expression )
return get_argspec ( entity )
return get_argspec ( get_entity ( expression ) )
def get_entity ( self , expression ) :
""" Return the object corresponding to expression evaluated
in a namespace spanning sys . modules and __main . dict__ .
"""
if expression :
namespace = sys . modules . copy ( )
namespace . update ( __main__ . __dict__ )
try :
return eval ( expression , namespace )
except BaseException :
# An uncaught exception closes idle, and eval can raise any
# exception, especially if user classes are involved.
return None
def _find_constructor ( class_ob ) :
" Find the nearest __init__() in the class tree. "
try :
return class_ob . __init__ . __func__
except AttributeError :
for base in class_ob . __bases__ :
init = _find_constructor ( base )
if init :
return init
return None
def get_entity ( expression ) :
""" Return the object corresponding to expression evaluated
in a namespace spanning sys . modules and __main . dict__ .
"""
if expression :
namespace = sys . modules . copy ( )
namespace . update ( __main__ . __dict__ )
try :
return eval ( expression , namespace )
except BaseException :
# An uncaught exception closes idle, and eval can raise any
# exception, especially if user classes are involved.
return None
# The following are used in both get_argspec and tests
_self_pat = re . compile ( ' self \ ,? \ s* ' )
_default_callable_argspec = " No docstring, see docs. "
def get_argspec ( ob ) :
""" Get a string describing the arguments for the given object,
only if it is callable . """
''' Return a string describing the arguments and return of a callable object.
For Python - coded functions and methods , the first line is introspected .
Delete ' self ' parameter for classes ( . __init__ ) and bound methods .
The last line is the first line of the doc string . For builtins , this typically
includes the arguments in addition to the return value .
'''
argspec = " "
if ob is not None and hasattr ( ob , ' __call__ ' ) :
if hasattr ( ob , ' __call__ ' ) :
if isinstance ( ob , type ) :
fob = _find_constructor ( ob )
if fob is None :
fob = lambda : None
elif isinstance ( ob , types . MethodType ) :
fob = ob . __func__
fob = getattr ( ob , ' __init__ ' , None )
elif isinstance ( ob . __call__ , types . MethodType ) :
fob = ob . __call__
else :
fob = ob
if isinstance ( fob , ( types . FunctionType , types . Lambda Type) ) :
if isinstance ( fob , ( types . FunctionType , types . Method Type) ) :
argspec = inspect . formatargspec ( * inspect . getfullargspec ( fob ) )
pat = re . compile ( ' self \ ,? \ s* ' )
argspec = pat . sub ( " " , argspec )
doc = getattr ( ob , " __doc__ " , " " )
if ( isinstance ( ob , ( type , types . MethodType ) ) or
isinstance ( ob . __call__ , types . MethodType ) ) :
argspec = _self_pat . sub ( " " , argspec )
if isinstance ( ob . __call__ , types . MethodType ) :
doc = ob . __call__ . __doc__
else :
doc = getattr ( ob , " __doc__ " , " " )
if doc :
doc = doc . lstrip ( )
pos = doc . find ( " \n " )
@ -154,13 +155,16 @@ def get_argspec(ob):
if argspec :
argspec + = " \n "
argspec + = doc [ : pos ]
if not argspec :
argspec = _default_callable_argspec
return argspec
#################################################
#
# Test code
#
# Test code tests CallTips.fetch_tip, get_entity, and get_argspec
def main ( ) :
# Putting expected in docstrings results in doubled tips for test
def t1 ( ) : " () "
def t2 ( a , b = None ) : " (a, b=None) "
def t3 ( a , * args ) : " (a, *args) "
@ -170,39 +174,88 @@ def main():
class TC ( object ) :
" (ai=None, *b) "
def __init__ ( self , ai = None , * b ) : " (ai=None, *b) "
def t1 ( self ) : " () "
def t2 ( self , ai , b = None ) : " (ai, b=None) "
def t3 ( self , ai , * args ) : " (ai, *args) "
def t4 ( self , * args ) : " (*args) "
def t5 ( self , ai , * args ) : " (ai, *args) "
def t6 ( self , ai , b = None , * args , * * kw ) : " (ai, b=None, *args, **kw) "
__main__ . __dict__ . update ( locals ( ) )
def test ( tests ) :
ct = CallTips ( )
failed = [ ]
for t in tests :
expected = t . __doc__ + " \n " + t . __doc__
name = t . __name__
# exercise fetch_tip(), not just get_argspec()
try :
qualified_name = " %s . %s " % ( t . __self__ . __class__ . __name__ , name )
except AttributeError :
qualified_name = name
argspec = ct . fetch_tip ( qualified_name )
if argspec != expected :
failed . append ( t )
fmt = " %s - expected %s , but got %s "
print ( fmt % ( t . __name__ , expected , get_argspec ( t ) ) )
print ( " %d of %d tests failed " % ( len ( failed ) , len ( tests ) ) )
def __init__ ( self , ai = None , * b ) : " (self, ai=None, *b) "
def t1 ( self ) : " (self) "
def t2 ( self , ai , b = None ) : " (self, ai, b=None) "
def t3 ( self , ai , * args ) : " (self, ai, *args) "
def t4 ( self , * args ) : " (self, *args) "
def t5 ( self , ai , * args ) : " (self, ai, *args) "
def t6 ( self , ai , b = None , * args , * * kw ) : " (self, ai, b=None, *args, **kw) "
@classmethod
def cm ( cls , a ) : " (cls, a) "
@staticmethod
def sm ( b ) : " (b) "
def __call__ ( self , ci ) : " (ci) "
tc = TC ( )
tests = ( t1 , t2 , t3 , t4 , t5 , t6 ,
TC , tc . t1 , tc . t2 , tc . t3 , tc . t4 , tc . t5 , tc . t6 )
test ( tests )
# Python classes that inherit builtin methods
class Int ( int ) : " Int(x[, base]) -> integer "
class List ( list ) : " List() -> new empty list "
# Simulate builtin with no docstring for default argspec test
class SB : __call__ = None
__main__ . __dict__ . update ( locals ( ) ) # required for get_entity eval()
num_tests = num_fail = 0
tip = CallTips ( ) . fetch_tip
def test ( expression , expected ) :
nonlocal num_tests , num_fail
num_tests + = 1
argspec = tip ( expression )
if argspec != expected :
num_fail + = 1
fmt = " %s - expected \n %r \n - but got \n %r "
print ( fmt % ( expression , expected , argspec ) )
def test_builtins ( ) :
# if first line of a possibly multiline compiled docstring changes,
# must change corresponding test string
test ( ' int ' , " int(x[, base]) -> integer " )
test ( ' Int ' , Int . __doc__ )
test ( ' types.MethodType ' , " method(function, instance) " )
test ( ' list ' , " list() -> new empty list " )
test ( ' List ' , List . __doc__ )
test ( ' list.__new__ ' ,
' T.__new__(S, ...) -> a new object with type S, a subtype of T ' )
test ( ' list.__init__ ' ,
' x.__init__(...) initializes x; see help(type(x)) for signature ' )
append_doc = " L.append(object) -> None -- append object to end "
test ( ' list.append ' , append_doc )
test ( ' [].append ' , append_doc )
test ( ' List.append ' , append_doc )
test ( ' SB() ' , _default_callable_argspec )
def test_funcs ( ) :
for func in ( t1 , t2 , t3 , t4 , t5 , t6 , TC , ) :
fdoc = func . __doc__
test ( func . __name__ , fdoc + " \n " + fdoc )
for func in ( TC . t1 , TC . t2 , TC . t3 , TC . t4 , TC . t5 , TC . t6 , TC . cm , TC . sm ) :
fdoc = func . __doc__
test ( ' TC. ' + func . __name__ , fdoc + " \n " + fdoc )
def test_methods ( ) :
for func in ( tc . t1 , tc . t2 , tc . t3 , tc . t4 , tc . t5 , tc . t6 ) :
fdoc = func . __doc__
test ( ' tc. ' + func . __name__ , _self_pat . sub ( " " , fdoc ) + " \n " + fdoc )
fdoc = tc . __call__ . __doc__
test ( ' tc ' , fdoc + " \n " + fdoc )
def test_non_callables ( ) :
# expression evaluates, but not to a callable
for expr in ( ' 0 ' , ' 0.0 ' ' num_tests ' , b ' num_tests ' , ' [] ' , ' {} ' ) :
test ( expr , ' ' )
# expression does not evaluate, but raises an exception
for expr in ( ' 1a ' , ' xyx ' , ' num_tests.xyz ' , ' [int][1] ' , ' {0:int}[1] ' ) :
test ( expr , ' ' )
test_builtins ( )
test_funcs ( )
test_non_callables ( )
test_methods ( )
print ( " %d of %d tests failed " % ( num_fail , num_tests ) )
if __name__ == ' __main__ ' :
main ( )