python-2.5.2/win32/Lib/idlelib/CallTips.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """CallTips.py - An IDLE Extension to Jog Your Memory
       
     2 
       
     3 Call Tips are floating windows which display function, class, and method
       
     4 parameter and docstring information when you type an opening parenthesis, and
       
     5 which disappear when you type a closing parenthesis.
       
     6 """
       
     7 import sys
       
     8 import types
       
     9 
       
    10 import CallTipWindow
       
    11 from HyperParser import HyperParser
       
    12 
       
    13 import __main__
       
    14 
       
    15 class CallTips:
       
    16 
       
    17     menudefs = [
       
    18         ('edit', [
       
    19             ("Show call tip", "<<force-open-calltip>>"),
       
    20         ])
       
    21     ]
       
    22 
       
    23     def __init__(self, editwin=None):
       
    24         if editwin is None:  # subprocess and test
       
    25             self.editwin = None
       
    26             return
       
    27         self.editwin = editwin
       
    28         self.text = editwin.text
       
    29         self.calltip = None
       
    30         self._make_calltip_window = self._make_tk_calltip_window
       
    31 
       
    32     def close(self):
       
    33         self._make_calltip_window = None
       
    34 
       
    35     def _make_tk_calltip_window(self):
       
    36         # See __init__ for usage
       
    37         return CallTipWindow.CallTip(self.text)
       
    38 
       
    39     def _remove_calltip_window(self, event=None):
       
    40         if self.calltip:
       
    41             self.calltip.hidetip()
       
    42             self.calltip = None
       
    43 
       
    44     def force_open_calltip_event(self, event):
       
    45         """Happens when the user really wants to open a CallTip, even if a
       
    46         function call is needed.
       
    47         """
       
    48         self.open_calltip(True)
       
    49 
       
    50     def try_open_calltip_event(self, event):
       
    51         """Happens when it would be nice to open a CallTip, but not really
       
    52         neccesary, for example after an opening bracket, so function calls
       
    53         won't be made.
       
    54         """
       
    55         self.open_calltip(False)
       
    56 
       
    57     def refresh_calltip_event(self, event):
       
    58         """If there is already a calltip window, check if it is still needed,
       
    59         and if so, reload it.
       
    60         """
       
    61         if self.calltip and self.calltip.is_active():
       
    62             self.open_calltip(False)
       
    63 
       
    64     def open_calltip(self, evalfuncs):
       
    65         self._remove_calltip_window()
       
    66 
       
    67         hp = HyperParser(self.editwin, "insert")
       
    68         sur_paren = hp.get_surrounding_brackets('(')
       
    69         if not sur_paren:
       
    70             return
       
    71         hp.set_index(sur_paren[0])
       
    72         name = hp.get_expression()
       
    73         if not name or (not evalfuncs and name.find('(') != -1):
       
    74             return
       
    75         arg_text = self.fetch_tip(name)
       
    76         if not arg_text:
       
    77             return
       
    78         self.calltip = self._make_calltip_window()
       
    79         self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
       
    80 
       
    81     def fetch_tip(self, name):
       
    82         """Return the argument list and docstring of a function or class
       
    83 
       
    84         If there is a Python subprocess, get the calltip there.  Otherwise,
       
    85         either fetch_tip() is running in the subprocess itself or it was called
       
    86         in an IDLE EditorWindow before any script had been run.
       
    87 
       
    88         The subprocess environment is that of the most recently run script.  If
       
    89         two unrelated modules are being edited some calltips in the current
       
    90         module may be inoperative if the module was not the last to run.
       
    91 
       
    92         """
       
    93         try:
       
    94             rpcclt = self.editwin.flist.pyshell.interp.rpcclt
       
    95         except:
       
    96             rpcclt = None
       
    97         if rpcclt:
       
    98             return rpcclt.remotecall("exec", "get_the_calltip",
       
    99                                      (name,), {})
       
   100         else:
       
   101             entity = self.get_entity(name)
       
   102             return get_arg_text(entity)
       
   103 
       
   104     def get_entity(self, name):
       
   105         "Lookup name in a namespace spanning sys.modules and __main.dict__"
       
   106         if name:
       
   107             namespace = sys.modules.copy()
       
   108             namespace.update(__main__.__dict__)
       
   109             try:
       
   110                 return eval(name, namespace)
       
   111             except:
       
   112                 return None
       
   113 
       
   114 def _find_constructor(class_ob):
       
   115     # Given a class object, return a function object used for the
       
   116     # constructor (ie, __init__() ) or None if we can't find one.
       
   117     try:
       
   118         return class_ob.__init__.im_func
       
   119     except AttributeError:
       
   120         for base in class_ob.__bases__:
       
   121             rc = _find_constructor(base)
       
   122             if rc is not None: return rc
       
   123     return None
       
   124 
       
   125 def get_arg_text(ob):
       
   126     """Get a string describing the arguments for the given object"""
       
   127     argText = ""
       
   128     if ob is not None:
       
   129         argOffset = 0
       
   130         if type(ob) in (types.ClassType, types.TypeType):
       
   131             # Look for the highest __init__ in the class chain.
       
   132             fob = _find_constructor(ob)
       
   133             if fob is None:
       
   134                 fob = lambda: None
       
   135             else:
       
   136                 argOffset = 1
       
   137         elif type(ob)==types.MethodType:
       
   138             # bit of a hack for methods - turn it into a function
       
   139             # but we drop the "self" param.
       
   140             fob = ob.im_func
       
   141             argOffset = 1
       
   142         else:
       
   143             fob = ob
       
   144         # Try and build one for Python defined functions
       
   145         if type(fob) in [types.FunctionType, types.LambdaType]:
       
   146             try:
       
   147                 realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
       
   148                 defaults = fob.func_defaults or []
       
   149                 defaults = list(map(lambda name: "=%s" % repr(name), defaults))
       
   150                 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
       
   151                 items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
       
   152                 if fob.func_code.co_flags & 0x4:
       
   153                     items.append("...")
       
   154                 if fob.func_code.co_flags & 0x8:
       
   155                     items.append("***")
       
   156                 argText = ", ".join(items)
       
   157                 argText = "(%s)" % argText
       
   158             except:
       
   159                 pass
       
   160         # See if we can use the docstring
       
   161         doc = getattr(ob, "__doc__", "")
       
   162         if doc:
       
   163             doc = doc.lstrip()
       
   164             pos = doc.find("\n")
       
   165             if pos < 0 or pos > 70:
       
   166                 pos = 70
       
   167             if argText:
       
   168                 argText += "\n"
       
   169             argText += doc[:pos]
       
   170     return argText
       
   171 
       
   172 #################################################
       
   173 #
       
   174 # Test code
       
   175 #
       
   176 if __name__=='__main__':
       
   177 
       
   178     def t1(): "()"
       
   179     def t2(a, b=None): "(a, b=None)"
       
   180     def t3(a, *args): "(a, ...)"
       
   181     def t4(*args): "(...)"
       
   182     def t5(a, *args): "(a, ...)"
       
   183     def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
       
   184 
       
   185     class TC:
       
   186         "(a=None, ...)"
       
   187         def __init__(self, a=None, *b): "(a=None, ...)"
       
   188         def t1(self): "()"
       
   189         def t2(self, a, b=None): "(a, b=None)"
       
   190         def t3(self, a, *args): "(a, ...)"
       
   191         def t4(self, *args): "(...)"
       
   192         def t5(self, a, *args): "(a, ...)"
       
   193         def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
       
   194 
       
   195     def test(tests):
       
   196         ct = CallTips()
       
   197         failed=[]
       
   198         for t in tests:
       
   199             expected = t.__doc__ + "\n" + t.__doc__
       
   200             name = t.__name__
       
   201             arg_text = ct.fetch_tip(name)
       
   202             if arg_text != expected:
       
   203                 failed.append(t)
       
   204                 print "%s - expected %s, but got %s" % (t, expected,
       
   205                                                         get_arg_text(entity))
       
   206         print "%d of %d tests failed" % (len(failed), len(tests))
       
   207 
       
   208     tc = TC()
       
   209     tests = (t1, t2, t3, t4, t5, t6,
       
   210              TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
       
   211 
       
   212     test(tests)