forked from VimPlug/jedi
added magic function names to completion, fixes nothing, but makes jedi more awesome :-D
This commit is contained in:
11
jedi/api.py
11
jedi/api.py
@@ -108,9 +108,9 @@ class Script(object):
|
|||||||
completions = []
|
completions = []
|
||||||
debug.dbg('possible scopes', scopes)
|
debug.dbg('possible scopes', scopes)
|
||||||
for s in scopes:
|
for s in scopes:
|
||||||
# TODO is this really the right way? just ignore the funcs? \
|
if s.isinstance(evaluate.Function):
|
||||||
# do the magic functions first? and then recheck here?
|
names = s.get_magic_method_names()
|
||||||
if not s.isinstance(evaluate.Function):
|
else:
|
||||||
if isinstance(s, imports.ImportPath):
|
if isinstance(s, imports.ImportPath):
|
||||||
if like == 'import':
|
if like == 'import':
|
||||||
l = self.module.get_line(self.pos[0])[:self.pos[1]]
|
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)
|
names = s.get_defined_names(on_import_stmt=True)
|
||||||
else:
|
else:
|
||||||
names = s.get_defined_names()
|
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
|
if not dot: # named_params have no dots
|
||||||
call_def = self.get_in_function_call()
|
call_def = self.get_in_function_call()
|
||||||
|
|||||||
306
jedi/builtin.py
306
jedi/builtin.py
@@ -6,13 +6,13 @@ import sys
|
|||||||
import os
|
import os
|
||||||
if is_py3k:
|
if is_py3k:
|
||||||
import io
|
import io
|
||||||
else:
|
import types
|
||||||
import types
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
import parsing
|
import parsing
|
||||||
import imports
|
import imports
|
||||||
|
import evaluate
|
||||||
|
|
||||||
|
|
||||||
def get_sys_path():
|
def get_sys_path():
|
||||||
@@ -157,7 +157,7 @@ class Parser(CachedModule):
|
|||||||
|
|
||||||
def _get_source(self):
|
def _get_source(self):
|
||||||
""" Override this abstract method """
|
""" 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):
|
def _load_mixins(self):
|
||||||
"""
|
"""
|
||||||
@@ -211,161 +211,162 @@ class Parser(CachedModule):
|
|||||||
mixin_dct['range'] = mixin_dct['xrange']
|
mixin_dct['range'] = mixin_dct['xrange']
|
||||||
return mixin_dct
|
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):
|
def _generate_code(scope, mixin_funcs={}, depth=0):
|
||||||
""" Base classes may contain the exact same object """
|
"""
|
||||||
if name in mixin_funcs:
|
Generate a string, which uses python syntax as an input to the
|
||||||
return False
|
PyFuzzyParser.
|
||||||
try:
|
"""
|
||||||
mro = cls.mro()
|
def get_doc(obj, indent=False):
|
||||||
except TypeError:
|
doc = inspect.getdoc(obj)
|
||||||
# this happens, if cls == type
|
if doc:
|
||||||
return False
|
doc = ('r"""\n%s\n"""\n' % doc)
|
||||||
for base in mro[1:]:
|
if indent:
|
||||||
try:
|
doc = parsing.indent_block(doc)
|
||||||
attr = getattr(base, name)
|
return doc
|
||||||
except AttributeError:
|
return ''
|
||||||
continue
|
|
||||||
if attr == comparison:
|
def is_in_base_classes(cls, name, comparison):
|
||||||
return True
|
""" Base classes may contain the exact same object """
|
||||||
|
if name in mixin_funcs:
|
||||||
return False
|
return False
|
||||||
|
try:
|
||||||
def get_scope_objects(names):
|
mro = cls.mro()
|
||||||
"""
|
except TypeError:
|
||||||
Looks for the names defined with dir() in an objects and divides
|
# this happens, if cls == type
|
||||||
them into different object types.
|
return False
|
||||||
"""
|
for base in mro[1:]:
|
||||||
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:
|
try:
|
||||||
path = scope.__file__
|
attr = getattr(base, name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
path = '?'
|
continue
|
||||||
code += '# Generated module %s from %s\n' % (scope.__name__, path)
|
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__',
|
code = ''
|
||||||
'__path__', '__package__']) \
|
if inspect.ismodule(scope): # generate comment where the code's from.
|
||||||
| set(['mro'])
|
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
|
names = set(dir(scope)) - set(['__file__', '__name__', '__doc__',
|
||||||
for name, cl in classes.items():
|
'__path__', '__package__']) \
|
||||||
bases = (c.__name__ for c in cl.__bases__)
|
| set(['mro'])
|
||||||
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'
|
|
||||||
|
|
||||||
# functions
|
classes, funcs, stmts, members = get_scope_objects(names)
|
||||||
for name, func in funcs.items():
|
|
||||||
params, ret = parse_function_doc(func)
|
# classes
|
||||||
if depth > 0:
|
for name, cl in classes.items():
|
||||||
params = 'self, ' + params
|
bases = (c.__name__ for c in cl.__bases__)
|
||||||
doc_str = get_doc(func, indent=True)
|
code += 'class %s(%s):\n' % (name, ','.join(bases))
|
||||||
|
if depth == 0:
|
||||||
try:
|
try:
|
||||||
mixin = mixin_funcs[name]
|
mixin = mixin_funcs[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# normal code generation
|
mixin = {}
|
||||||
code += 'def %s(%s):\n' % (name, params)
|
cl_code = _generate_code(cl, mixin, depth + 1)
|
||||||
code += doc_str
|
code += parsing.indent_block(cl_code)
|
||||||
code += parsing.indent_block('%s\n\n' % ret)
|
code += '\n'
|
||||||
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:]
|
|
||||||
|
|
||||||
# class members (functions) properties?
|
# functions
|
||||||
for name, func in members.items():
|
for name, func in funcs.items():
|
||||||
# recursion problem in properties TODO remove
|
params, ret = parse_function_doc(func)
|
||||||
if name in ['fget', 'fset', 'fdel']:
|
if depth > 0:
|
||||||
continue
|
params = 'self, ' + params
|
||||||
ret = 'pass'
|
doc_str = get_doc(func, indent=True)
|
||||||
code += '@property\ndef %s(self):\n' % (name)
|
try:
|
||||||
code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret)
|
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
|
# class members (functions) properties?
|
||||||
for name, value in stmts.items():
|
for name, func in members.items():
|
||||||
if is_py3k:
|
# recursion problem in properties TODO remove
|
||||||
file_type = io.TextIOWrapper
|
if name in ['fget', 'fset', 'fdel']:
|
||||||
else:
|
continue
|
||||||
file_type = types.FileType
|
ret = 'pass'
|
||||||
if type(value) == file_type:
|
code += '@property\ndef %s(self):\n' % (name)
|
||||||
value = 'open()'
|
code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret)
|
||||||
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:
|
# variables
|
||||||
#with open('writeout.py', 'w') as f:
|
for name, value in stmts.items():
|
||||||
# f.write(code)
|
if is_py3k:
|
||||||
#import sys
|
file_type = io.TextIOWrapper
|
||||||
#sys.stdout.write(code)
|
else:
|
||||||
#exit()
|
file_type = types.FileType
|
||||||
pass
|
if type(value) == file_type:
|
||||||
return code
|
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):
|
def parse_function_doc(func):
|
||||||
@@ -450,4 +451,23 @@ class Builtin(object):
|
|||||||
def scope(self):
|
def scope(self):
|
||||||
return self.builtin.parser.module
|
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()
|
Builtin = Builtin()
|
||||||
|
|||||||
@@ -457,6 +457,9 @@ class Function(use_metaclass(CachedMetaClass, parsing.Base)):
|
|||||||
return self
|
return self
|
||||||
return self._decorated_func
|
return self._decorated_func
|
||||||
|
|
||||||
|
def get_magic_method_names(self):
|
||||||
|
return builtin.Builtin.magic_function_names
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return getattr(self.base_func, name)
|
return getattr(self.base_func, name)
|
||||||
|
|
||||||
@@ -1561,10 +1564,8 @@ def follow_path(path, scope, call_scope, position=None):
|
|||||||
else:
|
else:
|
||||||
# The function must not be decorated with something else.
|
# The function must not be decorated with something else.
|
||||||
if scope.isinstance(Function):
|
if scope.isinstance(Function):
|
||||||
# TODO Check default function methods and return them.
|
result = scope.get_magic_method_names()
|
||||||
result = []
|
|
||||||
else:
|
else:
|
||||||
# TODO Check magic class methods and return them also.
|
|
||||||
# This is the typical lookup while chaining things.
|
# This is the typical lookup while chaining things.
|
||||||
if filter_private_variable(scope, call_scope, current):
|
if filter_private_variable(scope, call_scope, current):
|
||||||
return []
|
return []
|
||||||
|
|||||||
@@ -350,3 +350,10 @@ def annot_ret(a:3) -> 3:
|
|||||||
|
|
||||||
#? str()
|
#? str()
|
||||||
annot_ret('')
|
annot_ret('')
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# magic methods
|
||||||
|
# -----------------
|
||||||
|
def a(): pass
|
||||||
|
#? ['__closure__']
|
||||||
|
a.__closure__
|
||||||
|
|||||||
Reference in New Issue
Block a user