blob: b98e1c536ceab85256469c65c67e837fcb172a13 [file] [log] [blame]
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)