1
0
forked from VimPlug/jedi

added magic function names to completion, fixes nothing, but makes jedi more awesome :-D

This commit is contained in:
David Halter
2012-11-30 16:27:24 +01:00
parent ff827b4335
commit 99aed7ce91
4 changed files with 180 additions and 151 deletions

View File

@@ -108,9 +108,9 @@ class Script(object):
completions = []
debug.dbg('possible scopes', scopes)
for s in scopes:
# TODO is this really the right way? just ignore the funcs? \
# do the magic functions first? and then recheck here?
if not s.isinstance(evaluate.Function):
if s.isinstance(evaluate.Function):
names = s.get_magic_method_names()
else:
if isinstance(s, imports.ImportPath):
if like == 'import':
l = self.module.get_line(self.pos[0])[:self.pos[1]]
@@ -119,8 +119,9 @@ class Script(object):
names = s.get_defined_names(on_import_stmt=True)
else:
names = s.get_defined_names()
for c in names:
completions.append((c, s))
for c in names:
completions.append((c, s))
if not dot: # named_params have no dots
call_def = self.get_in_function_call()

View File

@@ -6,13 +6,13 @@ import sys
import os
if is_py3k:
import io
else:
import types
import types
import inspect
import debug
import parsing
import imports
import evaluate
def get_sys_path():
@@ -157,7 +157,7 @@ class Parser(CachedModule):
def _get_source(self):
""" Override this abstract method """
return self._generate_code(self.module, self._load_mixins())
return _generate_code(self.module, self._load_mixins())
def _load_mixins(self):
"""
@@ -211,161 +211,162 @@ class Parser(CachedModule):
mixin_dct['range'] = mixin_dct['xrange']
return mixin_dct
def _generate_code(self, scope, mixin_funcs, depth=0):
"""
Generate a string, which uses python syntax as an input to the
PyFuzzyParser.
"""
def get_doc(obj, indent=False):
doc = inspect.getdoc(obj)
if doc:
doc = ('r"""\n%s\n"""\n' % doc)
if indent:
doc = parsing.indent_block(doc)
return doc
return ''
def is_in_base_classes(cls, name, comparison):
""" Base classes may contain the exact same object """
if name in mixin_funcs:
return False
try:
mro = cls.mro()
except TypeError:
# this happens, if cls == type
return False
for base in mro[1:]:
try:
attr = getattr(base, name)
except AttributeError:
continue
if attr == comparison:
return True
def _generate_code(scope, mixin_funcs={}, depth=0):
"""
Generate a string, which uses python syntax as an input to the
PyFuzzyParser.
"""
def get_doc(obj, indent=False):
doc = inspect.getdoc(obj)
if doc:
doc = ('r"""\n%s\n"""\n' % doc)
if indent:
doc = parsing.indent_block(doc)
return doc
return ''
def is_in_base_classes(cls, name, comparison):
""" Base classes may contain the exact same object """
if name in mixin_funcs:
return False
def get_scope_objects(names):
"""
Looks for the names defined with dir() in an objects and divides
them into different object types.
"""
classes = {}
funcs = {}
stmts = {}
members = {}
for n in names:
try:
# this has a builtin_function_or_method
exe = getattr(scope, n)
except AttributeError:
# happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
members[n] = None
else:
if inspect.isclass(scope):
if is_in_base_classes(scope, n, exe):
continue
if inspect.isbuiltin(exe) or inspect.ismethod(exe) \
or inspect.ismethoddescriptor(exe):
funcs[n] = exe
elif inspect.isclass(exe):
classes[n] = exe
elif inspect.ismemberdescriptor(exe):
members[n] = exe
else:
stmts[n] = exe
return classes, funcs, stmts, members
code = ''
if inspect.ismodule(scope): # generate comment where the code's from.
try:
mro = cls.mro()
except TypeError:
# this happens, if cls == type
return False
for base in mro[1:]:
try:
path = scope.__file__
attr = getattr(base, name)
except AttributeError:
path = '?'
code += '# Generated module %s from %s\n' % (scope.__name__, path)
continue
if attr == comparison:
return True
return False
code += get_doc(scope)
def get_scope_objects(names):
"""
Looks for the names defined with dir() in an objects and divides
them into different object types.
"""
classes = {}
funcs = {}
stmts = {}
members = {}
for n in names:
try:
# this has a builtin_function_or_method
exe = getattr(scope, n)
except AttributeError:
# happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
members[n] = None
else:
if inspect.isclass(scope):
if is_in_base_classes(scope, n, exe):
continue
if inspect.isbuiltin(exe) or inspect.ismethod(exe) \
or inspect.ismethoddescriptor(exe):
funcs[n] = exe
elif inspect.isclass(exe):
classes[n] = exe
elif inspect.ismemberdescriptor(exe):
members[n] = exe
else:
stmts[n] = exe
return classes, funcs, stmts, members
names = set(dir(scope)) - set(['__file__', '__name__', '__doc__',
'__path__', '__package__']) \
| set(['mro'])
code = ''
if inspect.ismodule(scope): # generate comment where the code's from.
try:
path = scope.__file__
except AttributeError:
path = '?'
code += '# Generated module %s from %s\n' % (scope.__name__, path)
classes, funcs, stmts, members = get_scope_objects(names)
code += get_doc(scope)
# classes
for name, cl in classes.items():
bases = (c.__name__ for c in cl.__bases__)
code += 'class %s(%s):\n' % (name, ','.join(bases))
if depth == 0:
try:
mixin = mixin_funcs[name]
except KeyError:
mixin = {}
cl_code = self._generate_code(cl, mixin, depth + 1)
code += parsing.indent_block(cl_code)
code += '\n'
names = set(dir(scope)) - set(['__file__', '__name__', '__doc__',
'__path__', '__package__']) \
| set(['mro'])
# functions
for name, func in funcs.items():
params, ret = parse_function_doc(func)
if depth > 0:
params = 'self, ' + params
doc_str = get_doc(func, indent=True)
classes, funcs, stmts, members = get_scope_objects(names)
# classes
for name, cl in classes.items():
bases = (c.__name__ for c in cl.__bases__)
code += 'class %s(%s):\n' % (name, ','.join(bases))
if depth == 0:
try:
mixin = mixin_funcs[name]
except KeyError:
# normal code generation
code += 'def %s(%s):\n' % (name, params)
code += doc_str
code += parsing.indent_block('%s\n\n' % ret)
else:
# generation of code with mixins
# the parser only supports basic functions with a newline after
# the double dots
# find doc_str place
pos = re.search(r'\):\s*\n', mixin).end()
if pos is None:
raise Exception("Builtin function not parsed correctly")
code += mixin[:pos] + doc_str + mixin[pos:]
mixin = {}
cl_code = _generate_code(cl, mixin, depth + 1)
code += parsing.indent_block(cl_code)
code += '\n'
# class members (functions) properties?
for name, func in members.items():
# recursion problem in properties TODO remove
if name in ['fget', 'fset', 'fdel']:
continue
ret = 'pass'
code += '@property\ndef %s(self):\n' % (name)
code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret)
# functions
for name, func in funcs.items():
params, ret = parse_function_doc(func)
if depth > 0:
params = 'self, ' + params
doc_str = get_doc(func, indent=True)
try:
mixin = mixin_funcs[name]
except KeyError:
# normal code generation
code += 'def %s(%s):\n' % (name, params)
code += doc_str
code += parsing.indent_block('%s\n\n' % ret)
else:
# generation of code with mixins
# the parser only supports basic functions with a newline after
# the double dots
# find doc_str place
pos = re.search(r'\):\s*\n', mixin).end()
if pos is None:
raise Exception("Builtin function not parsed correctly")
code += mixin[:pos] + doc_str + mixin[pos:]
# variables
for name, value in stmts.items():
if is_py3k:
file_type = io.TextIOWrapper
else:
file_type = types.FileType
if type(value) == file_type:
value = 'open()'
elif name == 'None':
value = ''
elif type(value).__name__ in ['int', 'bool', 'float',
'dict', 'list', 'tuple']:
value = repr(value)
else:
# get the type, if the type is not simple.
mod = type(value).__module__
value = type(value).__name__ + '()'
if mod != '__builtin__':
value = '%s.%s' % (mod, value)
code += '%s = %s\n' % (name, value)
# class members (functions) properties?
for name, func in members.items():
# recursion problem in properties TODO remove
if name in ['fget', 'fset', 'fdel']:
continue
ret = 'pass'
code += '@property\ndef %s(self):\n' % (name)
code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret)
if depth == 0:
#with open('writeout.py', 'w') as f:
# f.write(code)
#import sys
#sys.stdout.write(code)
#exit()
pass
return code
# variables
for name, value in stmts.items():
if is_py3k:
file_type = io.TextIOWrapper
else:
file_type = types.FileType
if type(value) == file_type:
value = 'open()'
elif name == 'None':
value = ''
elif type(value).__name__ in ['int', 'bool', 'float',
'dict', 'list', 'tuple']:
value = repr(value)
else:
# get the type, if the type is not simple.
mod = type(value).__module__
value = type(value).__name__ + '()'
if mod != '__builtin__':
value = '%s.%s' % (mod, value)
code += '%s = %s\n' % (name, value)
if depth == 0:
#with open('writeout.py', 'w') as f:
# f.write(code)
#import sys
#sys.stdout.write(code)
#exit()
pass
return code
def parse_function_doc(func):
@@ -450,4 +451,23 @@ class Builtin(object):
def scope(self):
return self.builtin.parser.module
@property
def magic_function_names(self):
try:
return self._magic_function_names
except AttributeError:
# depth = 1 because this is not a module
class Container(object):
FunctionType = types.FunctionType
source = _generate_code(Container, depth=0)
parser = parsing.PyFuzzyParser(source, None)
# needed for caching (because of weakref)
module = self.magic_func_module = parser.module
typ = evaluate.follow_path(iter(['FunctionType']), module, module)
names = typ.pop().get_defined_names()
self._magic_function_names = names
return names
Builtin = Builtin()

View File

@@ -457,6 +457,9 @@ class Function(use_metaclass(CachedMetaClass, parsing.Base)):
return self
return self._decorated_func
def get_magic_method_names(self):
return builtin.Builtin.magic_function_names
def __getattr__(self, name):
return getattr(self.base_func, name)
@@ -1561,10 +1564,8 @@ def follow_path(path, scope, call_scope, position=None):
else:
# The function must not be decorated with something else.
if scope.isinstance(Function):
# TODO Check default function methods and return them.
result = []
result = scope.get_magic_method_names()
else:
# TODO Check magic class methods and return them also.
# This is the typical lookup while chaining things.
if filter_private_variable(scope, call_scope, current):
return []

View File

@@ -350,3 +350,10 @@ def annot_ret(a:3) -> 3:
#? str()
annot_ret('')
# -----------------
# magic methods
# -----------------
def a(): pass
#? ['__closure__']
a.__closure__