1
0
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:
Dave Halter
2013-12-29 03:05:05 +01:00
parent 4215e7934e
commit 0f6b5b222b
4 changed files with 106 additions and 69 deletions

View File

@@ -84,6 +84,7 @@ from jedi.evaluate import recursion
from jedi.evaluate.cache import memoize_default
from jedi import docstrings
from jedi.evaluate import dynamic
from jedi.evaluate import stdlib
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.
if (not position or isinstance(scope, (er.Array, er.Instance))
or start_scope != scope
and isinstance(start_scope, (pr.Function, er.Execution))):
and isinstance(start_scope, (pr.Function, er.FunctionExecution))):
return names
names_new = []
for n in names:
@@ -193,7 +194,7 @@ class Evaluator(object):
scope = scope.parent
# This is used, because subscopes (Flow scopes) would distort the
# 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
# Add star imports.
@@ -668,6 +669,11 @@ class Evaluator(object):
if obj.isinstance(er.Function):
obj = obj.get_decorated_func()
try:
return stdlib.execute(self, obj, params)
except stdlib.NotInStdLib:
pass
if obj.isinstance(er.Class):
# There maybe executions of executions.
return [er.Instance(self, obj, params)]
@@ -686,7 +692,7 @@ class Evaluator(object):
else:
debug.warning("no execution possible", obj)
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))
return imports.strip_imports(self, stmts)

View File

@@ -335,7 +335,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
from jedi import evaluate
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):
stmt = element._array.parent
else:
@@ -350,7 +350,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
search_names = ['append', 'extend', 'insert'] if is_list else \
['add', 'update']
comp_arr_parent = get_execution_parent(compare_array, er.Execution)
comp_arr_parent = get_execution_parent(compare_array, er.FunctionExecution)
possible_stmts = []
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
# dict. Executions are somewhat special in jedi, since they
# 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. \
get_statement_for_position(stmt.start_pos)
if stmt is None:

View File

@@ -67,7 +67,7 @@ class Instance(use_metaclass(CachedMetaClass, Executable)):
@memoize_default(None)
def _get_method_execution(self, func):
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):
"""
@@ -392,7 +392,7 @@ class Function(use_metaclass(CachedMetaClass, pr.IsScope)):
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.
@@ -401,69 +401,10 @@ class Execution(Executable):
multiple calls to functions and recursion has to be avoided. But this is
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=())
@recursion.execution_recursion_decorator
def get_return_types(self, evaluate_generator=False):
""" Get the return types of a function. """
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 """
func = self.base
# Feed the listeners, with the params.
for listener in func.listeners:
listener.execute(self._get_params())
@@ -479,7 +420,7 @@ class Execution(Executable):
@memoize_default(default=())
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.
This needs to be here, because Instance can have __init__ functions,
which act the same way as normal functions.

90
jedi/evaluate/stdlib.py Normal file
View 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,
}
}