forked from VimPlug/jedi
add a stdlib module to make it easy to write functions in pure python (instead of evaluating them)
This commit is contained in:
@@ -84,6 +84,7 @@ from jedi.evaluate import recursion
|
|||||||
from jedi.evaluate.cache import memoize_default
|
from jedi.evaluate.cache import memoize_default
|
||||||
from jedi import docstrings
|
from jedi import docstrings
|
||||||
from jedi.evaluate import dynamic
|
from jedi.evaluate import dynamic
|
||||||
|
from jedi.evaluate import stdlib
|
||||||
|
|
||||||
|
|
||||||
def get_defined_names_for_position(scope, position=None, start_scope=None):
|
def get_defined_names_for_position(scope, position=None, start_scope=None):
|
||||||
@@ -106,7 +107,7 @@ def get_defined_names_for_position(scope, position=None, start_scope=None):
|
|||||||
# because class variables are always valid and the `self.` variables, too.
|
# because class variables are always valid and the `self.` variables, too.
|
||||||
if (not position or isinstance(scope, (er.Array, er.Instance))
|
if (not position or isinstance(scope, (er.Array, er.Instance))
|
||||||
or start_scope != scope
|
or start_scope != scope
|
||||||
and isinstance(start_scope, (pr.Function, er.Execution))):
|
and isinstance(start_scope, (pr.Function, er.FunctionExecution))):
|
||||||
return names
|
return names
|
||||||
names_new = []
|
names_new = []
|
||||||
for n in names:
|
for n in names:
|
||||||
@@ -193,7 +194,7 @@ class Evaluator(object):
|
|||||||
scope = scope.parent
|
scope = scope.parent
|
||||||
# This is used, because subscopes (Flow scopes) would distort the
|
# This is used, because subscopes (Flow scopes) would distort the
|
||||||
# results.
|
# results.
|
||||||
if scope and scope.isinstance(er.Function, pr.Function, er.Execution):
|
if scope and scope.isinstance(er.Function, pr.Function, er.FunctionExecution):
|
||||||
in_func_scope = scope
|
in_func_scope = scope
|
||||||
|
|
||||||
# Add star imports.
|
# Add star imports.
|
||||||
@@ -668,6 +669,11 @@ class Evaluator(object):
|
|||||||
if obj.isinstance(er.Function):
|
if obj.isinstance(er.Function):
|
||||||
obj = obj.get_decorated_func()
|
obj = obj.get_decorated_func()
|
||||||
|
|
||||||
|
try:
|
||||||
|
return stdlib.execute(self, obj, params)
|
||||||
|
except stdlib.NotInStdLib:
|
||||||
|
pass
|
||||||
|
|
||||||
if obj.isinstance(er.Class):
|
if obj.isinstance(er.Class):
|
||||||
# There maybe executions of executions.
|
# There maybe executions of executions.
|
||||||
return [er.Instance(self, obj, params)]
|
return [er.Instance(self, obj, params)]
|
||||||
@@ -686,7 +692,7 @@ class Evaluator(object):
|
|||||||
else:
|
else:
|
||||||
debug.warning("no execution possible", obj)
|
debug.warning("no execution possible", obj)
|
||||||
else:
|
else:
|
||||||
stmts = er.Execution(self, obj, params).get_return_types(evaluate_generator)
|
stmts = er.FunctionExecution(self, obj, params).get_return_types(evaluate_generator)
|
||||||
|
|
||||||
debug.dbg('execute: %s in %s' % (stmts, self))
|
debug.dbg('execute: %s in %s' % (stmts, self))
|
||||||
return imports.strip_imports(self, stmts)
|
return imports.strip_imports(self, stmts)
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
from jedi import evaluate
|
from jedi import evaluate
|
||||||
|
|
||||||
def get_execution_parent(element, *stop_classes):
|
def get_execution_parent(element, *stop_classes):
|
||||||
""" Used to get an Instance/Execution parent """
|
""" Used to get an Instance/FunctionExecution parent """
|
||||||
if isinstance(element, er.Array):
|
if isinstance(element, er.Array):
|
||||||
stmt = element._array.parent
|
stmt = element._array.parent
|
||||||
else:
|
else:
|
||||||
@@ -350,7 +350,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
|
|
||||||
search_names = ['append', 'extend', 'insert'] if is_list else \
|
search_names = ['append', 'extend', 'insert'] if is_list else \
|
||||||
['add', 'update']
|
['add', 'update']
|
||||||
comp_arr_parent = get_execution_parent(compare_array, er.Execution)
|
comp_arr_parent = get_execution_parent(compare_array, er.FunctionExecution)
|
||||||
|
|
||||||
possible_stmts = []
|
possible_stmts = []
|
||||||
res = []
|
res = []
|
||||||
@@ -364,7 +364,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
# can search for the same statement, that is in the module
|
# can search for the same statement, that is in the module
|
||||||
# dict. Executions are somewhat special in jedi, since they
|
# dict. Executions are somewhat special in jedi, since they
|
||||||
# literally copy the contents of a function.
|
# literally copy the contents of a function.
|
||||||
if isinstance(comp_arr_parent, er.Execution):
|
if isinstance(comp_arr_parent, er.FunctionExecution):
|
||||||
stmt = comp_arr_parent. \
|
stmt = comp_arr_parent. \
|
||||||
get_statement_for_position(stmt.start_pos)
|
get_statement_for_position(stmt.start_pos)
|
||||||
if stmt is None:
|
if stmt is None:
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
|
|||||||
@memoize_default(None)
|
@memoize_default(None)
|
||||||
def _get_method_execution(self, func):
|
def _get_method_execution(self, func):
|
||||||
func = InstanceElement(self._evaluator, self, func, True)
|
func = InstanceElement(self._evaluator, self, func, True)
|
||||||
return Execution(self._evaluator, func, self.var_args)
|
return FunctionExecution(self._evaluator, func, self.var_args)
|
||||||
|
|
||||||
def _get_func_self_name(self, func):
|
def _get_func_self_name(self, func):
|
||||||
"""
|
"""
|
||||||
@@ -392,7 +392,7 @@ class Function(use_metaclass(CachedMetaClass, pr.IsScope)):
|
|||||||
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
|
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
|
||||||
|
|
||||||
|
|
||||||
class Execution(Executable):
|
class FunctionExecution(Executable):
|
||||||
"""
|
"""
|
||||||
This class is used to evaluate functions and their returns.
|
This class is used to evaluate functions and their returns.
|
||||||
|
|
||||||
@@ -401,69 +401,10 @@ class Execution(Executable):
|
|||||||
multiple calls to functions and recursion has to be avoided. But this is
|
multiple calls to functions and recursion has to be avoided. But this is
|
||||||
responsibility of the decorators.
|
responsibility of the decorators.
|
||||||
"""
|
"""
|
||||||
def _follow_var_arg(self, index):
|
|
||||||
try:
|
|
||||||
stmt = self.var_args[index]
|
|
||||||
except IndexError:
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
if isinstance(stmt, pr.Statement):
|
|
||||||
return self._evaluator.eval_statement(stmt)
|
|
||||||
else:
|
|
||||||
return [stmt] # just some arbitrary object
|
|
||||||
|
|
||||||
@memoize_default(default=())
|
@memoize_default(default=())
|
||||||
@recursion.execution_recursion_decorator
|
@recursion.execution_recursion_decorator
|
||||||
def get_return_types(self, evaluate_generator=False):
|
def get_return_types(self, evaluate_generator=False):
|
||||||
""" Get the return types of a function. """
|
func = self.base
|
||||||
base = self.base
|
|
||||||
stmts = []
|
|
||||||
if base.parent == builtin.Builtin.scope \
|
|
||||||
and not isinstance(base, (Generator, Array)):
|
|
||||||
func_name = str(base.name)
|
|
||||||
|
|
||||||
# some implementations of builtins:
|
|
||||||
if func_name == 'getattr':
|
|
||||||
# follow the first param
|
|
||||||
objects = self._follow_var_arg(0)
|
|
||||||
names = self._follow_var_arg(1)
|
|
||||||
for obj in objects:
|
|
||||||
if not isinstance(obj, (Instance, Class, pr.Module)):
|
|
||||||
debug.warning('getattr called without instance')
|
|
||||||
continue
|
|
||||||
|
|
||||||
for arr_name in names:
|
|
||||||
if not isinstance(arr_name, Instance):
|
|
||||||
debug.warning('getattr called without str')
|
|
||||||
continue
|
|
||||||
if len(arr_name.var_args) != 1:
|
|
||||||
debug.warning('jedi getattr is too simple')
|
|
||||||
key = arr_name.var_args[0]
|
|
||||||
stmts += self._evaluator.follow_path(iter([key]), [obj], base)
|
|
||||||
return stmts
|
|
||||||
elif func_name == 'type':
|
|
||||||
# otherwise it would be a metaclass
|
|
||||||
if len(self.var_args) == 1:
|
|
||||||
objects = self._follow_var_arg(0)
|
|
||||||
return [o.base for o in objects if isinstance(o, Instance)]
|
|
||||||
elif func_name == 'super':
|
|
||||||
# TODO make this able to detect multiple inheritance super
|
|
||||||
accept = (pr.Function,)
|
|
||||||
func = self.var_args.get_parent_until(accept)
|
|
||||||
if func.isinstance(*accept):
|
|
||||||
cls = func.get_parent_until(accept + (pr.Class,),
|
|
||||||
include_current=False)
|
|
||||||
if isinstance(cls, pr.Class):
|
|
||||||
cls = Class(self._evaluator, cls)
|
|
||||||
su = cls.get_super_classes()
|
|
||||||
if su:
|
|
||||||
return [Instance(self._evaluator, su[0])]
|
|
||||||
return []
|
|
||||||
|
|
||||||
return self._get_function_returns(base, evaluate_generator)
|
|
||||||
|
|
||||||
def _get_function_returns(self, func, evaluate_generator):
|
|
||||||
""" A normal Function execution """
|
|
||||||
# Feed the listeners, with the params.
|
# Feed the listeners, with the params.
|
||||||
for listener in func.listeners:
|
for listener in func.listeners:
|
||||||
listener.execute(self._get_params())
|
listener.execute(self._get_params())
|
||||||
@@ -479,7 +420,7 @@ class Execution(Executable):
|
|||||||
@memoize_default(default=())
|
@memoize_default(default=())
|
||||||
def _get_params(self):
|
def _get_params(self):
|
||||||
"""
|
"""
|
||||||
This returns the params for an Execution/Instance and is injected as a
|
This returns the params for an TODO and is injected as a
|
||||||
'hack' into the pr.Function class.
|
'hack' into the pr.Function class.
|
||||||
This needs to be here, because Instance can have __init__ functions,
|
This needs to be here, because Instance can have __init__ functions,
|
||||||
which act the same way as normal functions.
|
which act the same way as normal functions.
|
||||||
|
|||||||
90
jedi/evaluate/stdlib.py
Normal file
90
jedi/evaluate/stdlib.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
"""
|
||||||
|
Implementations of standard library functions, because it's not possible to
|
||||||
|
understand them with Jedi.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from jedi.evaluate import builtin
|
||||||
|
from jedi.evaluate import representation as er
|
||||||
|
from jedi.parser import representation as pr
|
||||||
|
from jedi import debug
|
||||||
|
|
||||||
|
|
||||||
|
class NotInStdLib(LookupError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def execute(evaluator, obj, params):
|
||||||
|
if not isinstance(obj, (er.Generator, er.Array)):
|
||||||
|
obj_name = str(obj.name)
|
||||||
|
if obj.parent == builtin.Builtin.scope:
|
||||||
|
# for now we just support builtin functions.
|
||||||
|
try:
|
||||||
|
return _implemented['builtins'][obj_name](evaluator, obj, params)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
raise NotInStdLib()
|
||||||
|
|
||||||
|
|
||||||
|
def _follow_param(evaluator, params, index):
|
||||||
|
try:
|
||||||
|
stmt = params[index]
|
||||||
|
except IndexError:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
if isinstance(stmt, pr.Statement):
|
||||||
|
return evaluator.eval_statement(stmt)
|
||||||
|
else:
|
||||||
|
return [stmt] # just some arbitrary object
|
||||||
|
|
||||||
|
|
||||||
|
def builtins_getattr(evaluator, obj, params):
|
||||||
|
stmts = []
|
||||||
|
# follow the first param
|
||||||
|
objects = _follow_param(evaluator, params, 0)
|
||||||
|
names = _follow_param(evaluator, params, 1)
|
||||||
|
for obj in objects:
|
||||||
|
if not isinstance(obj, (er.Instance, er.Class, pr.Module)):
|
||||||
|
debug.warning('getattr called without instance')
|
||||||
|
continue
|
||||||
|
|
||||||
|
for arr_name in names:
|
||||||
|
if not isinstance(arr_name, er.Instance):
|
||||||
|
debug.warning('getattr called without str')
|
||||||
|
continue
|
||||||
|
if len(arr_name.var_args) != 1:
|
||||||
|
debug.warning('jedi getattr is too simple')
|
||||||
|
key = arr_name.var_args[0]
|
||||||
|
stmts += evaluator.follow_path(iter([key]), [obj], obj)
|
||||||
|
return stmts
|
||||||
|
|
||||||
|
|
||||||
|
def builtins_type(evaluator, obj, params):
|
||||||
|
if len(params) == 1:
|
||||||
|
# otherwise it would be a metaclass... maybe someday...
|
||||||
|
objects = _follow_param(evaluator, params, 0)
|
||||||
|
return [o.base for o in objects if isinstance(o, er.Instance)]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def builtins_super(evaluator, obj, params):
|
||||||
|
# TODO make this able to detect multiple inheritance super
|
||||||
|
accept = (pr.Function,)
|
||||||
|
func = params.get_parent_until(accept)
|
||||||
|
if func.isinstance(*accept):
|
||||||
|
cls = func.get_parent_until(accept + (pr.Class,),
|
||||||
|
include_current=False)
|
||||||
|
if isinstance(cls, pr.Class):
|
||||||
|
cls = er.Class(evaluator, cls)
|
||||||
|
su = cls.get_super_classes()
|
||||||
|
if su:
|
||||||
|
return [er.Instance(evaluator, su[0])]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
_implemented = {
|
||||||
|
'builtins': {
|
||||||
|
'getattr': builtins_getattr,
|
||||||
|
'type': builtins_type,
|
||||||
|
'super': builtins_super,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user