PyXR

c:\python24\lib \ idlelib \ CallTips.py



0001 """CallTips.py - An IDLE Extension to Jog Your Memory
0002 
0003 Call Tips are floating windows which display function, class, and method
0004 parameter and docstring information when you type an opening parenthesis, and
0005 which disappear when you type a closing parenthesis.
0006 
0007 Future plans include extending the functionality to include class attributes.
0008 
0009 """
0010 import sys
0011 import string
0012 import types
0013 
0014 import CallTipWindow
0015 
0016 import __main__
0017 
0018 class CallTips:
0019 
0020     menudefs = [
0021     ]
0022 
0023     def __init__(self, editwin=None):
0024         if editwin is None:  # subprocess and test
0025             self.editwin = None
0026             return
0027         self.editwin = editwin
0028         self.text = editwin.text
0029         self.calltip = None
0030         self._make_calltip_window = self._make_tk_calltip_window
0031 
0032     def close(self):
0033         self._make_calltip_window = None
0034 
0035     def _make_tk_calltip_window(self):
0036         # See __init__ for usage
0037         return CallTipWindow.CallTip(self.text)
0038 
0039     def _remove_calltip_window(self):
0040         if self.calltip:
0041             self.calltip.hidetip()
0042             self.calltip = None
0043 
0044     def paren_open_event(self, event):
0045         self._remove_calltip_window()
0046         name = self.get_name_at_cursor()
0047         arg_text = self.fetch_tip(name)
0048         if arg_text:
0049             self.calltip_start = self.text.index("insert")
0050             self.calltip = self._make_calltip_window()
0051             self.calltip.showtip(arg_text)
0052         return "" #so the event is handled normally.
0053 
0054     def paren_close_event(self, event):
0055         # Now just hides, but later we should check if other
0056         # paren'd expressions remain open.
0057         self._remove_calltip_window()
0058         return "" #so the event is handled normally.
0059 
0060     def check_calltip_cancel_event(self, event):
0061         if self.calltip:
0062             # If we have moved before the start of the calltip,
0063             # or off the calltip line, then cancel the tip.
0064             # (Later need to be smarter about multi-line, etc)
0065             if self.text.compare("insert", "<=", self.calltip_start) or \
0066                self.text.compare("insert", ">", self.calltip_start
0067                                  + " lineend"):
0068                 self._remove_calltip_window()
0069         return "" #so the event is handled normally.
0070 
0071     def calltip_cancel_event(self, event):
0072         self._remove_calltip_window()
0073         return "" #so the event is handled normally.
0074 
0075     __IDCHARS = "._" + string.ascii_letters + string.digits
0076 
0077     def get_name_at_cursor(self):
0078         idchars = self.__IDCHARS
0079         str = self.text.get("insert linestart", "insert")
0080         i = len(str)
0081         while i and str[i-1] in idchars:
0082             i -= 1
0083         return str[i:]
0084 
0085     def fetch_tip(self, name):
0086         """Return the argument list and docstring of a function or class
0087 
0088         If there is a Python subprocess, get the calltip there.  Otherwise,
0089         either fetch_tip() is running in the subprocess itself or it was called
0090         in an IDLE EditorWindow before any script had been run.
0091 
0092         The subprocess environment is that of the most recently run script.  If
0093         two unrelated modules are being edited some calltips in the current
0094         module may be inoperative if the module was not the last to run.
0095 
0096         """
0097         try:
0098             rpcclt = self.editwin.flist.pyshell.interp.rpcclt
0099         except:
0100             rpcclt = None
0101         if rpcclt:
0102             return rpcclt.remotecall("exec", "get_the_calltip",
0103                                      (name,), {})
0104         else:
0105             entity = self.get_entity(name)
0106             return get_arg_text(entity)
0107 
0108     def get_entity(self, name):
0109         "Lookup name in a namespace spanning sys.modules and __main.dict__"
0110         if name:
0111             namespace = sys.modules.copy()
0112             namespace.update(__main__.__dict__)
0113             try:
0114                 return eval(name, namespace)
0115             except:
0116                 return None
0117 
0118 def _find_constructor(class_ob):
0119     # Given a class object, return a function object used for the
0120     # constructor (ie, __init__() ) or None if we can't find one.
0121     try:
0122         return class_ob.__init__.im_func
0123     except AttributeError:
0124         for base in class_ob.__bases__:
0125             rc = _find_constructor(base)
0126             if rc is not None: return rc
0127     return None
0128 
0129 def get_arg_text(ob):
0130     "Get a string describing the arguments for the given object"
0131     argText = ""
0132     if ob is not None:
0133         argOffset = 0
0134         if type(ob)==types.ClassType:
0135             # Look for the highest __init__ in the class chain.
0136             fob = _find_constructor(ob)
0137             if fob is None:
0138                 fob = lambda: None
0139             else:
0140                 argOffset = 1
0141         elif type(ob)==types.MethodType:
0142             # bit of a hack for methods - turn it into a function
0143             # but we drop the "self" param.
0144             fob = ob.im_func
0145             argOffset = 1
0146         else:
0147             fob = ob
0148         # Try and build one for Python defined functions
0149         if type(fob) in [types.FunctionType, types.LambdaType]:
0150             try:
0151                 realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount]
0152                 defaults = fob.func_defaults or []
0153                 defaults = list(map(lambda name: "=%s" % name, defaults))
0154                 defaults = [""] * (len(realArgs)-len(defaults)) + defaults
0155                 items = map(lambda arg, dflt: arg+dflt, realArgs, defaults)
0156                 if fob.func_code.co_flags & 0x4:
0157                     items.append("...")
0158                 if fob.func_code.co_flags & 0x8:
0159                     items.append("***")
0160                 argText = ", ".join(items)
0161                 argText = "(%s)" % argText
0162             except:
0163                 pass
0164         # See if we can use the docstring
0165         doc = getattr(ob, "__doc__", "")
0166         if doc:
0167             doc = doc.lstrip()
0168             pos = doc.find("\n")
0169             if pos < 0 or pos > 70:
0170                 pos = 70
0171             if argText:
0172                 argText += "\n"
0173             argText += doc[:pos]
0174     return argText
0175 
0176 #################################################
0177 #
0178 # Test code
0179 #
0180 if __name__=='__main__':
0181 
0182     def t1(): "()"
0183     def t2(a, b=None): "(a, b=None)"
0184     def t3(a, *args): "(a, ...)"
0185     def t4(*args): "(...)"
0186     def t5(a, *args): "(a, ...)"
0187     def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)"
0188 
0189     class TC:
0190         "(a=None, ...)"
0191         def __init__(self, a=None, *b): "(a=None, ...)"
0192         def t1(self): "()"
0193         def t2(self, a, b=None): "(a, b=None)"
0194         def t3(self, a, *args): "(a, ...)"
0195         def t4(self, *args): "(...)"
0196         def t5(self, a, *args): "(a, ...)"
0197         def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)"
0198 
0199     def test(tests):
0200         ct = CallTips()
0201         failed=[]
0202         for t in tests:
0203             expected = t.__doc__ + "\n" + t.__doc__
0204             name = t.__name__
0205             arg_text = ct.fetch_tip(name)
0206             if arg_text != expected:
0207                 failed.append(t)
0208                 print "%s - expected %s, but got %s" % (t, expected,
0209                                                         get_arg_text(entity))
0210         print "%d of %d tests failed" % (len(failed), len(tests))
0211 
0212     tc = TC()
0213     tests = (t1, t2, t3, t4, t5, t6,
0214              TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6)
0215 
0216     test(tests)
0217 

Generated by PyXR 0.9.4
SourceForge.net Logo