import os.path import inspect import sys # completion types. TYPE_IMPORT = '0' TYPE_CLASS = '1' TYPE_FUNCTION = '2' TYPE_ATTR = '3' TYPE_BUILTIN = '4' TYPE_PARAM = '5' def _imp(name, log=None): try: return __import__(name) except: if '.' in name: sub = name[0:name.rfind('.')] if log is not None: log.AddContent('Unable to import', name, 'trying with', sub) # log.AddContent('PYTHONPATH:') # log.AddContent('\n'.join(sorted(sys.path))) log.AddException() return _imp(sub, log) else: s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path) if log is not None: log.AddContent(s) log.AddException() raise ImportError(s) IS_IPY = False if sys.platform == 'cli': IS_IPY = True _old_imp = _imp def _imp(name, log=None): # We must add a reference in clr for .Net import clr # @UnresolvedImport initial_name = name while '.' in name: try: clr.AddReference(name) break # If it worked, that's OK. except: name = name[0:name.rfind('.')] else: try: clr.AddReference(name) except: pass # That's OK (not dot net module). return _old_imp(initial_name, log) def GetFile(mod): f = None try: f = inspect.getsourcefile(mod) or inspect.getfile(mod) except: if hasattr(mod, '__file__'): f = mod.__file__ if f.lower(f[-4:]) in ['.pyc', '.pyo']: filename = f[:-4] + '.py' if os.path.exists(filename): f = filename return f def Find(name, log=None): f = None mod = _imp(name, log) parent = mod foundAs = '' if inspect.ismodule(mod): f = GetFile(mod) components = name.split('.') old_comp = None for comp in components[1:]: try: # this happens in the following case: # we have mx.DateTime.mxDateTime.mxDateTime.pyd # but after importing it, mx.DateTime.mxDateTime shadows access to mxDateTime.pyd mod = getattr(mod, comp) except AttributeError: if old_comp != comp: raise if inspect.ismodule(mod): f = GetFile(mod) else: if len(foundAs) > 0: foundAs = foundAs + '.' foundAs = foundAs + comp old_comp = comp return f, mod, parent, foundAs def GenerateTip(data, log=None): data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') f, mod, parent, foundAs = Find(data, log) # print_ >> open('temp.txt', 'w'), f tips = GenerateImportsTipForModule(mod) return f, tips def CheckChar(c): if c == '-' or c == '.': return '_' return c def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True): ''' @param obj_to_complete: the object from where we should get the completions @param dirComps: if passed, we should not 'dir' the object and should just iterate those passed as a parameter @param getattr: the way to get a given object from the obj_to_complete (used for the completer) @param filter: a callable that receives the name and decides if it should be appended or not to the results @return: list of tuples, so that each tuple represents a completion with: name, doc, args, type (from the TYPE_* constants) ''' ret = [] if dirComps is None: dirComps = dir(obj_to_complete) if hasattr(obj_to_complete, '__dict__'): dirComps.append('__dict__') if hasattr(obj_to_complete, '__class__'): dirComps.append('__class__') getCompleteInfo = True if len(dirComps) > 1000: # ok, we don't want to let our users wait forever... # no complete info for you... getCompleteInfo = False dontGetDocsOn = (float, int, str, tuple, list) for d in dirComps: if d is None: continue if not filter(d): continue args = '' try: obj = getattr(obj_to_complete, d) except: # just ignore and get it without aditional info ret.append((d, '', args, TYPE_BUILTIN)) else: if getCompleteInfo: retType = TYPE_BUILTIN # check if we have to get docs getDoc = True for class_ in dontGetDocsOn: if isinstance(obj, class_): getDoc = False break doc = '' if getDoc: # no need to get this info... too many constants are defined and # makes things much slower (passing all that through sockets takes quite some time) try: doc = inspect.getdoc(obj) if doc is None: doc = '' except: # may happen on jython when checking java classes (so, just ignore it) doc = '' if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj): try: args, vargs, kwargs, defaults = inspect.getargspec(obj) except: args, vargs, kwargs, defaults = (('self',), None, None, None) if defaults is not None: start_defaults_at = len(args) - len(defaults) r = '' for i, a in enumerate(args): if len(r) > 0: r = r + ', ' r = r + str(a) if defaults is not None and i >= start_defaults_at: default = defaults[i - start_defaults_at] r += '=' +str(default) others = '' if vargs: others += '*' + vargs if kwargs: if others: others+= ', ' others += '**' + kwargs if others: r+= ', ' args = '(%s%s)' % (r, others) retType = TYPE_FUNCTION elif inspect.isclass(obj): retType = TYPE_CLASS elif inspect.ismodule(obj): retType = TYPE_IMPORT else: retType = TYPE_ATTR # add token and doc to return - assure only strings. ret.append((d, doc, args, retType)) else: # getCompleteInfo == False if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj): retType = TYPE_FUNCTION elif inspect.isclass(obj): retType = TYPE_CLASS elif inspect.ismodule(obj): retType = TYPE_IMPORT else: retType = TYPE_ATTR # ok, no complete info, let's try to do this as fast and clean as possible # so, no docs for this kind of information, only the signatures ret.append((d, '', str(args), retType)) return ret if __name__ == '__main__': # To use when we have some object: i.e.: obj_to_complete=MyModel.objects temp = ''' def %(method_name)s%(args)s: """ %(doc)s """ ''' for entry in GenerateImportsTipForModule(obj_to_complete): import textwrap doc = textwrap.dedent(entry[1]) lines = [] for line in doc.splitlines(): lines.append(' ' + line) doc = '\n'.join(lines) print(temp % dict(method_name=entry[0], args=entry[2] or '(self)', doc=doc))