mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 22:44:27 +08:00
Remove the function context to a separate module.
This commit is contained in:
@@ -18,6 +18,7 @@ from jedi.evaluate import compiled
|
|||||||
from jedi.evaluate.filters import ParamName
|
from jedi.evaluate.filters import ParamName
|
||||||
from jedi.evaluate.imports import ImportName
|
from jedi.evaluate.imports import ImportName
|
||||||
from jedi.evaluate.context import instance
|
from jedi.evaluate.context import instance
|
||||||
|
from jedi.evaluate.context.function import FunctionContext, FunctionExecutionContext
|
||||||
from jedi.api.keywords import KeywordName
|
from jedi.api.keywords import KeywordName
|
||||||
|
|
||||||
|
|
||||||
@@ -354,10 +355,10 @@ class BaseDefinition(object):
|
|||||||
if context is None:
|
if context is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if isinstance(context, er.FunctionExecutionContext):
|
if isinstance(context, FunctionExecutionContext):
|
||||||
# TODO the function context should be a part of the function
|
# TODO the function context should be a part of the function
|
||||||
# execution context.
|
# execution context.
|
||||||
context = er.FunctionContext(
|
context = FunctionContext(
|
||||||
self._evaluator, context.parent_context, context.tree_node)
|
self._evaluator, context.parent_context, context.tree_node)
|
||||||
return Definition(self._evaluator, context.name)
|
return Definition(self._evaluator, context.name)
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ from jedi.evaluate.filters import TreeNameDefinition, ParamName
|
|||||||
from jedi.evaluate.context.instance import AnonymousInstance, BoundMethod
|
from jedi.evaluate.context.instance import AnonymousInstance, BoundMethod
|
||||||
from jedi.evaluate.context import ContextualizedName, ContextualizedNode, \
|
from jedi.evaluate.context import ContextualizedName, ContextualizedNode, \
|
||||||
ContextSet, NO_CONTEXTS, iterate_contexts
|
ContextSet, NO_CONTEXTS, iterate_contexts
|
||||||
|
from jedi.evaluate.context.function import FunctionContext
|
||||||
from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \
|
from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \
|
||||||
eval_node, check_tuple_assignments
|
eval_node, check_tuple_assignments
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
@@ -208,7 +209,7 @@ class Evaluator(object):
|
|||||||
if type_ == 'classdef':
|
if type_ == 'classdef':
|
||||||
return [er.ClassContext(self, context, name.parent)]
|
return [er.ClassContext(self, context, name.parent)]
|
||||||
elif type_ == 'funcdef':
|
elif type_ == 'funcdef':
|
||||||
return [er.FunctionContext(self, context, name.parent)]
|
return [FunctionContext(self, context, name.parent)]
|
||||||
|
|
||||||
if type_ == 'expr_stmt':
|
if type_ == 'expr_stmt':
|
||||||
is_simple_name = name.parent.type not in ('power', 'trailer')
|
is_simple_name = name.parent.type not in ('power', 'trailer')
|
||||||
@@ -330,7 +331,7 @@ class Evaluator(object):
|
|||||||
parent_context.parent_context, scope_node
|
parent_context.parent_context, scope_node
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
func = er.FunctionContext(
|
func = FunctionContext(
|
||||||
self,
|
self,
|
||||||
parent_context,
|
parent_context,
|
||||||
scope_node
|
scope_node
|
||||||
|
|||||||
@@ -552,7 +552,7 @@ def _create_from_name(evaluator, module, compiled_object, name):
|
|||||||
try:
|
try:
|
||||||
faked = fake.get_faked(evaluator, module, obj, parent_context=compiled_object, name=name)
|
faked = fake.get_faked(evaluator, module, obj, parent_context=compiled_object, name=name)
|
||||||
if faked.type == 'funcdef':
|
if faked.type == 'funcdef':
|
||||||
from jedi.evaluate.representation import FunctionContext
|
from jedi.evaluate.context.function import FunctionContext
|
||||||
return FunctionContext(evaluator, compiled_object, faked)
|
return FunctionContext(evaluator, compiled_object, faked)
|
||||||
except fake.FakeDoesNotExist:
|
except fake.FakeDoesNotExist:
|
||||||
pass
|
pass
|
||||||
@@ -633,7 +633,7 @@ def create(evaluator, obj, parent_context=None, module=None, faked=None):
|
|||||||
try:
|
try:
|
||||||
faked = fake.get_faked(evaluator, module, obj, parent_context=parent_context)
|
faked = fake.get_faked(evaluator, module, obj, parent_context=parent_context)
|
||||||
if faked.type == 'funcdef':
|
if faked.type == 'funcdef':
|
||||||
from jedi.evaluate.representation import FunctionContext
|
from jedi.evaluate.context.function import FunctionContext
|
||||||
return FunctionContext(evaluator, parent_context, faked)
|
return FunctionContext(evaluator, parent_context, faked)
|
||||||
except fake.FakeDoesNotExist:
|
except fake.FakeDoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|||||||
226
jedi/evaluate/context/function.py
Normal file
226
jedi/evaluate/context/function.py
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
from parso.python import tree
|
||||||
|
|
||||||
|
from jedi._compatibility import use_metaclass
|
||||||
|
from jedi import debug
|
||||||
|
from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass
|
||||||
|
from jedi.evaluate import compiled
|
||||||
|
from jedi.evaluate import recursion
|
||||||
|
from jedi.evaluate import docstrings
|
||||||
|
from jedi.evaluate import pep0484
|
||||||
|
from jedi.evaluate import param
|
||||||
|
from jedi.evaluate import flow_analysis
|
||||||
|
from jedi.evaluate import helpers
|
||||||
|
from jedi.evaluate import iterable
|
||||||
|
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
||||||
|
ContextName, AbstractNameDefinition, ParamName
|
||||||
|
from jedi.evaluate import context
|
||||||
|
from jedi.evaluate.context import ContextualizedNode, NO_CONTEXTS, ContextSet
|
||||||
|
from jedi import parser_utils
|
||||||
|
from jedi.evaluate.parser_cache import get_yield_exprs
|
||||||
|
|
||||||
|
|
||||||
|
class LambdaName(AbstractNameDefinition):
|
||||||
|
string_name = '<lambda>'
|
||||||
|
|
||||||
|
def __init__(self, lambda_context):
|
||||||
|
self._lambda_context = lambda_context
|
||||||
|
self.parent_context = lambda_context.parent_context
|
||||||
|
|
||||||
|
def start_pos(self):
|
||||||
|
return self._lambda_context.tree_node.start_pos
|
||||||
|
|
||||||
|
def infer(self):
|
||||||
|
return ContextSet(self._lambda_context)
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
||||||
|
"""
|
||||||
|
Needed because of decorators. Decorators are evaluated here.
|
||||||
|
"""
|
||||||
|
api_type = 'function'
|
||||||
|
|
||||||
|
def __init__(self, evaluator, parent_context, funcdef):
|
||||||
|
""" This should not be called directly """
|
||||||
|
super(FunctionContext, self).__init__(evaluator, parent_context)
|
||||||
|
self.tree_node = funcdef
|
||||||
|
|
||||||
|
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||||
|
if search_global:
|
||||||
|
yield ParserTreeFilter(
|
||||||
|
self.evaluator,
|
||||||
|
context=self,
|
||||||
|
until_position=until_position,
|
||||||
|
origin_scope=origin_scope
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
scope = self.py__class__()
|
||||||
|
for filter in scope.get_filters(search_global=False, origin_scope=origin_scope):
|
||||||
|
yield filter
|
||||||
|
|
||||||
|
def infer_function_execution(self, function_execution):
|
||||||
|
"""
|
||||||
|
Created to be used by inheritance.
|
||||||
|
"""
|
||||||
|
yield_exprs = get_yield_exprs(self.evaluator, self.tree_node)
|
||||||
|
if yield_exprs:
|
||||||
|
return ContextSet(iterable.Generator(self.evaluator, function_execution))
|
||||||
|
else:
|
||||||
|
return function_execution.get_return_values()
|
||||||
|
|
||||||
|
def get_function_execution(self, arguments=None):
|
||||||
|
if arguments is None:
|
||||||
|
arguments = param.AnonymousArguments()
|
||||||
|
|
||||||
|
return FunctionExecutionContext(self.evaluator, self.parent_context, self, arguments)
|
||||||
|
|
||||||
|
def py__call__(self, arguments):
|
||||||
|
function_execution = self.get_function_execution(arguments)
|
||||||
|
return self.infer_function_execution(function_execution)
|
||||||
|
|
||||||
|
def py__class__(self):
|
||||||
|
# This differentiation is only necessary for Python2. Python3 does not
|
||||||
|
# use a different method class.
|
||||||
|
if isinstance(parser_utils.get_parent_scope(self.tree_node), tree.Class):
|
||||||
|
name = 'METHOD_CLASS'
|
||||||
|
else:
|
||||||
|
name = 'FUNCTION_CLASS'
|
||||||
|
return compiled.get_special_object(self.evaluator, name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
if self.tree_node.type == 'lambdef':
|
||||||
|
return LambdaName(self)
|
||||||
|
return ContextName(self, self.tree_node.name)
|
||||||
|
|
||||||
|
def get_param_names(self):
|
||||||
|
function_execution = self.get_function_execution()
|
||||||
|
return [ParamName(function_execution, param.name)
|
||||||
|
for param in self.tree_node.get_params()]
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionExecutionContext(context.TreeContext):
|
||||||
|
"""
|
||||||
|
This class is used to evaluate functions and their returns.
|
||||||
|
|
||||||
|
This is the most complicated class, because it contains the logic to
|
||||||
|
transfer parameters. It is even more complicated, because there may be
|
||||||
|
multiple calls to functions and recursion has to be avoided. But this is
|
||||||
|
responsibility of the decorators.
|
||||||
|
"""
|
||||||
|
function_execution_filter = FunctionExecutionFilter
|
||||||
|
|
||||||
|
def __init__(self, evaluator, parent_context, function_context, var_args):
|
||||||
|
super(FunctionExecutionContext, self).__init__(evaluator, parent_context)
|
||||||
|
self.function_context = function_context
|
||||||
|
self.tree_node = function_context.tree_node
|
||||||
|
self.var_args = var_args
|
||||||
|
|
||||||
|
@evaluator_method_cache(default=NO_CONTEXTS)
|
||||||
|
@recursion.execution_recursion_decorator()
|
||||||
|
def get_return_values(self, check_yields=False):
|
||||||
|
funcdef = self.tree_node
|
||||||
|
if funcdef.type == 'lambdef':
|
||||||
|
return self.evaluator.eval_element(self, funcdef.children[-1])
|
||||||
|
|
||||||
|
if check_yields:
|
||||||
|
context_set = NO_CONTEXTS
|
||||||
|
returns = get_yield_exprs(self.evaluator, funcdef)
|
||||||
|
else:
|
||||||
|
returns = funcdef.iter_return_stmts()
|
||||||
|
context_set = docstrings.infer_return_types(self.function_context)
|
||||||
|
context_set |= pep0484.infer_return_types(self.function_context)
|
||||||
|
|
||||||
|
for r in returns:
|
||||||
|
check = flow_analysis.reachability_check(self, funcdef, r)
|
||||||
|
if check is flow_analysis.UNREACHABLE:
|
||||||
|
debug.dbg('Return unreachable: %s', r)
|
||||||
|
else:
|
||||||
|
if check_yields:
|
||||||
|
context_set |= ContextSet.from_sets(
|
||||||
|
lazy_context.infer()
|
||||||
|
for lazy_context in self._eval_yield(r)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
children = r.children
|
||||||
|
except AttributeError:
|
||||||
|
context_set |= ContextSet(compiled.create(self.evaluator, None))
|
||||||
|
else:
|
||||||
|
context_set |= self.eval_node(children[1])
|
||||||
|
if check is flow_analysis.REACHABLE:
|
||||||
|
debug.dbg('Return reachable: %s', r)
|
||||||
|
break
|
||||||
|
return context_set
|
||||||
|
|
||||||
|
def _eval_yield(self, yield_expr):
|
||||||
|
if yield_expr.type == 'keyword':
|
||||||
|
# `yield` just yields None.
|
||||||
|
yield context.LazyKnownContext(compiled.create(self.evaluator, None))
|
||||||
|
return
|
||||||
|
|
||||||
|
node = yield_expr.children[1]
|
||||||
|
if node.type == 'yield_arg': # It must be a yield from.
|
||||||
|
cn = ContextualizedNode(self, node.children[1])
|
||||||
|
for lazy_context in cn.infer().iterate(cn):
|
||||||
|
yield lazy_context
|
||||||
|
else:
|
||||||
|
yield context.LazyTreeContext(self, node)
|
||||||
|
|
||||||
|
@recursion.execution_recursion_decorator(default=iter([]))
|
||||||
|
def get_yield_values(self):
|
||||||
|
for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef',
|
||||||
|
'while_stmt', 'if_stmt'))
|
||||||
|
for y in get_yield_exprs(self.evaluator, self.tree_node)]
|
||||||
|
|
||||||
|
# Calculate if the yields are placed within the same for loop.
|
||||||
|
yields_order = []
|
||||||
|
last_for_stmt = None
|
||||||
|
for yield_, for_stmt in for_parents:
|
||||||
|
# For really simple for loops we can predict the order. Otherwise
|
||||||
|
# we just ignore it.
|
||||||
|
parent = for_stmt.parent
|
||||||
|
if parent.type == 'suite':
|
||||||
|
parent = parent.parent
|
||||||
|
if for_stmt.type == 'for_stmt' and parent == self.tree_node \
|
||||||
|
and parser_utils.for_stmt_defines_one_name(for_stmt): # Simplicity for now.
|
||||||
|
if for_stmt == last_for_stmt:
|
||||||
|
yields_order[-1][1].append(yield_)
|
||||||
|
else:
|
||||||
|
yields_order.append((for_stmt, [yield_]))
|
||||||
|
elif for_stmt == self.tree_node:
|
||||||
|
yields_order.append((None, [yield_]))
|
||||||
|
else:
|
||||||
|
types = self.get_return_values(check_yields=True)
|
||||||
|
if types:
|
||||||
|
yield context.LazyKnownContexts(types)
|
||||||
|
return
|
||||||
|
last_for_stmt = for_stmt
|
||||||
|
|
||||||
|
for for_stmt, yields in yields_order:
|
||||||
|
if for_stmt is None:
|
||||||
|
# No for_stmt, just normal yields.
|
||||||
|
for yield_ in yields:
|
||||||
|
for result in self._eval_yield(yield_):
|
||||||
|
yield result
|
||||||
|
else:
|
||||||
|
input_node = for_stmt.get_testlist()
|
||||||
|
cn = ContextualizedNode(self, input_node)
|
||||||
|
ordered = cn.infer().iterate(cn)
|
||||||
|
ordered = list(ordered)
|
||||||
|
for lazy_context in ordered:
|
||||||
|
dct = {str(for_stmt.children[1].value): lazy_context.infer()}
|
||||||
|
with helpers.predefine_names(self, for_stmt, dct):
|
||||||
|
for yield_in_same_for_stmt in yields:
|
||||||
|
for result in self._eval_yield(yield_in_same_for_stmt):
|
||||||
|
yield result
|
||||||
|
|
||||||
|
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
||||||
|
yield self.function_execution_filter(self.evaluator, self,
|
||||||
|
until_position=until_position,
|
||||||
|
origin_scope=origin_scope)
|
||||||
|
|
||||||
|
@evaluator_method_cache()
|
||||||
|
def get_params(self):
|
||||||
|
return self.var_args.get_params(self)
|
||||||
|
|
||||||
|
|
||||||
@@ -10,12 +10,13 @@ from jedi.evaluate.cache import evaluator_method_cache
|
|||||||
from jedi.evaluate.param import AbstractArguments, AnonymousArguments
|
from jedi.evaluate.param import AbstractArguments, AnonymousArguments
|
||||||
from jedi.cache import memoize_method
|
from jedi.cache import memoize_method
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
|
from jedi.evaluate.context.function import FunctionExecutionContext, FunctionContext
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi.parser_utils import get_parent_scope
|
from jedi.parser_utils import get_parent_scope
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceFunctionExecution(er.FunctionExecutionContext):
|
class InstanceFunctionExecution(FunctionExecutionContext):
|
||||||
def __init__(self, instance, parent_context, function_context, var_args):
|
def __init__(self, instance, parent_context, function_context, var_args):
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
var_args = InstanceVarArgs(self, var_args)
|
var_args = InstanceVarArgs(self, var_args)
|
||||||
@@ -24,7 +25,7 @@ class InstanceFunctionExecution(er.FunctionExecutionContext):
|
|||||||
instance.evaluator, parent_context, function_context, var_args)
|
instance.evaluator, parent_context, function_context, var_args)
|
||||||
|
|
||||||
|
|
||||||
class AnonymousInstanceFunctionExecution(er.FunctionExecutionContext):
|
class AnonymousInstanceFunctionExecution(FunctionExecutionContext):
|
||||||
function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter
|
function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter
|
||||||
|
|
||||||
def __init__(self, instance, parent_context, function_context, var_args):
|
def __init__(self, instance, parent_context, function_context, var_args):
|
||||||
@@ -253,7 +254,7 @@ class CompiledInstanceName(compiled.CompiledName):
|
|||||||
@iterator_to_context_set
|
@iterator_to_context_set
|
||||||
def infer(self):
|
def infer(self):
|
||||||
for result_context in super(CompiledInstanceName, self).infer():
|
for result_context in super(CompiledInstanceName, self).infer():
|
||||||
if isinstance(result_context, er.FunctionContext):
|
if isinstance(result_context, FunctionContext):
|
||||||
parent_context = result_context.parent_context
|
parent_context = result_context.parent_context
|
||||||
while parent_context.is_class():
|
while parent_context.is_class():
|
||||||
parent_context = parent_context.parent_context
|
parent_context = parent_context.parent_context
|
||||||
@@ -285,7 +286,7 @@ class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
|
|||||||
self._evaluator, self._instance, self._compiled_object, name)
|
self._evaluator, self._instance, self._compiled_object, name)
|
||||||
|
|
||||||
|
|
||||||
class BoundMethod(er.FunctionContext):
|
class BoundMethod(FunctionContext):
|
||||||
def __init__(self, evaluator, instance, class_context, *args, **kwargs):
|
def __init__(self, evaluator, instance, class_context, *args, **kwargs):
|
||||||
super(BoundMethod, self).__init__(evaluator, *args, **kwargs)
|
super(BoundMethod, self).__init__(evaluator, *args, **kwargs)
|
||||||
self._instance = instance
|
self._instance = instance
|
||||||
@@ -333,7 +334,7 @@ class LazyInstanceClassName(LazyInstanceName):
|
|||||||
@iterator_to_context_set
|
@iterator_to_context_set
|
||||||
def infer(self):
|
def infer(self):
|
||||||
for result_context in super(LazyInstanceClassName, self).infer():
|
for result_context in super(LazyInstanceClassName, self).infer():
|
||||||
if isinstance(result_context, er.FunctionContext):
|
if isinstance(result_context, FunctionContext):
|
||||||
# Classes are never used to resolve anything within the
|
# Classes are never used to resolve anything within the
|
||||||
# functions. Only other functions and modules will resolve
|
# functions. Only other functions and modules will resolve
|
||||||
# those things.
|
# those things.
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ def _evaluate_for_statement_string(module_context, string):
|
|||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
from jedi.evaluate.representation import FunctionContext
|
from jedi.evaluate.context.function import FunctionContext
|
||||||
function_context = FunctionContext(
|
function_context = FunctionContext(
|
||||||
module_context.evaluator,
|
module_context.evaluator,
|
||||||
module_context,
|
module_context,
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ def _get_possible_nodes(module_context, func_string_name):
|
|||||||
|
|
||||||
|
|
||||||
def _check_name_for_execution(evaluator, context, compare_node, name, trailer):
|
def _check_name_for_execution(evaluator, context, compare_node, name, trailer):
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate.context.function import FunctionExecutionContext
|
||||||
|
|
||||||
def create_func_excs():
|
def create_func_excs():
|
||||||
arglist = trailer.children[1]
|
arglist = trailer.children[1]
|
||||||
@@ -175,7 +175,7 @@ def _check_name_for_execution(evaluator, context, compare_node, name, trailer):
|
|||||||
if compare_node == value_node:
|
if compare_node == value_node:
|
||||||
for func_execution in create_func_excs():
|
for func_execution in create_func_excs():
|
||||||
yield func_execution
|
yield func_execution
|
||||||
elif isinstance(value.parent_context, er.FunctionExecutionContext) and \
|
elif isinstance(value.parent_context, FunctionExecutionContext) and \
|
||||||
compare_node.type == 'funcdef':
|
compare_node.type == 'funcdef':
|
||||||
# Here we're trying to find decorators by checking the first
|
# Here we're trying to find decorators by checking the first
|
||||||
# parameter. It's not very generic though. Should find a better
|
# parameter. It's not very generic though. Should find a better
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope):
|
|||||||
>>> filters[4].values() #doctest: +ELLIPSIS
|
>>> filters[4].values() #doctest: +ELLIPSIS
|
||||||
[<CompiledName: ...>, ...]
|
[<CompiledName: ...>, ...]
|
||||||
"""
|
"""
|
||||||
from jedi.evaluate.representation import FunctionExecutionContext
|
from jedi.evaluate.context.function import FunctionExecutionContext
|
||||||
while context is not None:
|
while context is not None:
|
||||||
# Names in methods cannot be resolved within the class.
|
# Names in methods cannot be resolved within the class.
|
||||||
for filter in context.get_filters(
|
for filter in context.get_filters(
|
||||||
|
|||||||
@@ -40,28 +40,15 @@ py__doc__(include_call_signature: Returns the docstring for a context.
|
|||||||
import os
|
import os
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from parso.python import tree
|
|
||||||
|
|
||||||
from jedi._compatibility import use_metaclass
|
from jedi._compatibility import use_metaclass
|
||||||
from jedi import debug
|
|
||||||
from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass
|
from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import recursion
|
|
||||||
from jedi.evaluate import docstrings
|
|
||||||
from jedi.evaluate import pep0484
|
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
from jedi.evaluate import flow_analysis
|
|
||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate.filters import ParserTreeFilter, TreeNameDefinition, \
|
||||||
from jedi.evaluate import iterable
|
DictFilter, ContextName, AbstractNameDefinition, AnonymousInstanceParamName
|
||||||
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
|
|
||||||
DictFilter, ContextName, AbstractNameDefinition, \
|
|
||||||
ParamName, AnonymousInstanceParamName, TreeNameDefinition
|
|
||||||
from jedi.evaluate import context
|
from jedi.evaluate import context
|
||||||
from jedi.evaluate.context import ContextualizedNode, NO_CONTEXTS, \
|
from jedi.evaluate.context import NO_CONTEXTS, ContextSet, iterator_to_context_set
|
||||||
ContextSet, iterator_to_context_set
|
|
||||||
from jedi import parser_utils
|
|
||||||
from jedi.evaluate.parser_cache import get_yield_exprs
|
|
||||||
|
|
||||||
|
|
||||||
def apply_py__get__(context, base_context):
|
def apply_py__get__(context, base_context):
|
||||||
@@ -213,211 +200,6 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|||||||
return ContextName(self, self.tree_node.name)
|
return ContextName(self, self.tree_node.name)
|
||||||
|
|
||||||
|
|
||||||
class LambdaName(AbstractNameDefinition):
|
|
||||||
string_name = '<lambda>'
|
|
||||||
|
|
||||||
def __init__(self, lambda_context):
|
|
||||||
self._lambda_context = lambda_context
|
|
||||||
self.parent_context = lambda_context.parent_context
|
|
||||||
|
|
||||||
def start_pos(self):
|
|
||||||
return self._lambda_context.tree_node.start_pos
|
|
||||||
|
|
||||||
def infer(self):
|
|
||||||
return ContextSet(self._lambda_context)
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext)):
|
|
||||||
"""
|
|
||||||
Needed because of decorators. Decorators are evaluated here.
|
|
||||||
"""
|
|
||||||
api_type = 'function'
|
|
||||||
|
|
||||||
def __init__(self, evaluator, parent_context, funcdef):
|
|
||||||
""" This should not be called directly """
|
|
||||||
super(FunctionContext, self).__init__(evaluator, parent_context)
|
|
||||||
self.tree_node = funcdef
|
|
||||||
|
|
||||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
|
||||||
if search_global:
|
|
||||||
yield ParserTreeFilter(
|
|
||||||
self.evaluator,
|
|
||||||
context=self,
|
|
||||||
until_position=until_position,
|
|
||||||
origin_scope=origin_scope
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
scope = self.py__class__()
|
|
||||||
for filter in scope.get_filters(search_global=False, origin_scope=origin_scope):
|
|
||||||
yield filter
|
|
||||||
|
|
||||||
def infer_function_execution(self, function_execution):
|
|
||||||
"""
|
|
||||||
Created to be used by inheritance.
|
|
||||||
"""
|
|
||||||
yield_exprs = get_yield_exprs(self.evaluator, self.tree_node)
|
|
||||||
if yield_exprs:
|
|
||||||
return ContextSet(iterable.Generator(self.evaluator, function_execution))
|
|
||||||
else:
|
|
||||||
return function_execution.get_return_values()
|
|
||||||
|
|
||||||
def get_function_execution(self, arguments=None):
|
|
||||||
if arguments is None:
|
|
||||||
arguments = param.AnonymousArguments()
|
|
||||||
|
|
||||||
return FunctionExecutionContext(self.evaluator, self.parent_context, self, arguments)
|
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
|
||||||
function_execution = self.get_function_execution(arguments)
|
|
||||||
return self.infer_function_execution(function_execution)
|
|
||||||
|
|
||||||
def py__class__(self):
|
|
||||||
# This differentiation is only necessary for Python2. Python3 does not
|
|
||||||
# use a different method class.
|
|
||||||
if isinstance(parser_utils.get_parent_scope(self.tree_node), tree.Class):
|
|
||||||
name = 'METHOD_CLASS'
|
|
||||||
else:
|
|
||||||
name = 'FUNCTION_CLASS'
|
|
||||||
return compiled.get_special_object(self.evaluator, name)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
if self.tree_node.type == 'lambdef':
|
|
||||||
return LambdaName(self)
|
|
||||||
return ContextName(self, self.tree_node.name)
|
|
||||||
|
|
||||||
def get_param_names(self):
|
|
||||||
function_execution = self.get_function_execution()
|
|
||||||
return [ParamName(function_execution, param.name)
|
|
||||||
for param in self.tree_node.get_params()]
|
|
||||||
|
|
||||||
|
|
||||||
class FunctionExecutionContext(context.TreeContext):
|
|
||||||
"""
|
|
||||||
This class is used to evaluate functions and their returns.
|
|
||||||
|
|
||||||
This is the most complicated class, because it contains the logic to
|
|
||||||
transfer parameters. It is even more complicated, because there may be
|
|
||||||
multiple calls to functions and recursion has to be avoided. But this is
|
|
||||||
responsibility of the decorators.
|
|
||||||
"""
|
|
||||||
function_execution_filter = FunctionExecutionFilter
|
|
||||||
|
|
||||||
def __init__(self, evaluator, parent_context, function_context, var_args):
|
|
||||||
super(FunctionExecutionContext, self).__init__(evaluator, parent_context)
|
|
||||||
self.function_context = function_context
|
|
||||||
self.tree_node = function_context.tree_node
|
|
||||||
self.var_args = var_args
|
|
||||||
|
|
||||||
@evaluator_method_cache(default=NO_CONTEXTS)
|
|
||||||
@recursion.execution_recursion_decorator()
|
|
||||||
def get_return_values(self, check_yields=False):
|
|
||||||
funcdef = self.tree_node
|
|
||||||
if funcdef.type == 'lambdef':
|
|
||||||
return self.evaluator.eval_element(self, funcdef.children[-1])
|
|
||||||
|
|
||||||
if check_yields:
|
|
||||||
context_set = NO_CONTEXTS
|
|
||||||
returns = get_yield_exprs(self.evaluator, funcdef)
|
|
||||||
else:
|
|
||||||
returns = funcdef.iter_return_stmts()
|
|
||||||
context_set = docstrings.infer_return_types(self.function_context)
|
|
||||||
context_set |= pep0484.infer_return_types(self.function_context)
|
|
||||||
|
|
||||||
for r in returns:
|
|
||||||
check = flow_analysis.reachability_check(self, funcdef, r)
|
|
||||||
if check is flow_analysis.UNREACHABLE:
|
|
||||||
debug.dbg('Return unreachable: %s', r)
|
|
||||||
else:
|
|
||||||
if check_yields:
|
|
||||||
context_set |= ContextSet.from_sets(
|
|
||||||
lazy_context.infer()
|
|
||||||
for lazy_context in self._eval_yield(r)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
children = r.children
|
|
||||||
except AttributeError:
|
|
||||||
context_set |= ContextSet(compiled.create(self.evaluator, None))
|
|
||||||
else:
|
|
||||||
context_set |= self.eval_node(children[1])
|
|
||||||
if check is flow_analysis.REACHABLE:
|
|
||||||
debug.dbg('Return reachable: %s', r)
|
|
||||||
break
|
|
||||||
return context_set
|
|
||||||
|
|
||||||
def _eval_yield(self, yield_expr):
|
|
||||||
if yield_expr.type == 'keyword':
|
|
||||||
# `yield` just yields None.
|
|
||||||
yield context.LazyKnownContext(compiled.create(self.evaluator, None))
|
|
||||||
return
|
|
||||||
|
|
||||||
node = yield_expr.children[1]
|
|
||||||
if node.type == 'yield_arg': # It must be a yield from.
|
|
||||||
cn = ContextualizedNode(self, node.children[1])
|
|
||||||
for lazy_context in cn.infer().iterate(cn):
|
|
||||||
yield lazy_context
|
|
||||||
else:
|
|
||||||
yield context.LazyTreeContext(self, node)
|
|
||||||
|
|
||||||
@recursion.execution_recursion_decorator(default=iter([]))
|
|
||||||
def get_yield_values(self):
|
|
||||||
for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef',
|
|
||||||
'while_stmt', 'if_stmt'))
|
|
||||||
for y in get_yield_exprs(self.evaluator, self.tree_node)]
|
|
||||||
|
|
||||||
# Calculate if the yields are placed within the same for loop.
|
|
||||||
yields_order = []
|
|
||||||
last_for_stmt = None
|
|
||||||
for yield_, for_stmt in for_parents:
|
|
||||||
# For really simple for loops we can predict the order. Otherwise
|
|
||||||
# we just ignore it.
|
|
||||||
parent = for_stmt.parent
|
|
||||||
if parent.type == 'suite':
|
|
||||||
parent = parent.parent
|
|
||||||
if for_stmt.type == 'for_stmt' and parent == self.tree_node \
|
|
||||||
and parser_utils.for_stmt_defines_one_name(for_stmt): # Simplicity for now.
|
|
||||||
if for_stmt == last_for_stmt:
|
|
||||||
yields_order[-1][1].append(yield_)
|
|
||||||
else:
|
|
||||||
yields_order.append((for_stmt, [yield_]))
|
|
||||||
elif for_stmt == self.tree_node:
|
|
||||||
yields_order.append((None, [yield_]))
|
|
||||||
else:
|
|
||||||
types = self.get_return_values(check_yields=True)
|
|
||||||
if types:
|
|
||||||
yield context.LazyKnownContexts(types)
|
|
||||||
return
|
|
||||||
last_for_stmt = for_stmt
|
|
||||||
|
|
||||||
for for_stmt, yields in yields_order:
|
|
||||||
if for_stmt is None:
|
|
||||||
# No for_stmt, just normal yields.
|
|
||||||
for yield_ in yields:
|
|
||||||
for result in self._eval_yield(yield_):
|
|
||||||
yield result
|
|
||||||
else:
|
|
||||||
input_node = for_stmt.get_testlist()
|
|
||||||
cn = ContextualizedNode(self, input_node)
|
|
||||||
ordered = cn.infer().iterate(cn)
|
|
||||||
ordered = list(ordered)
|
|
||||||
for lazy_context in ordered:
|
|
||||||
dct = {str(for_stmt.children[1].value): lazy_context.infer()}
|
|
||||||
with helpers.predefine_names(self, for_stmt, dct):
|
|
||||||
for yield_in_same_for_stmt in yields:
|
|
||||||
for result in self._eval_yield(yield_in_same_for_stmt):
|
|
||||||
yield result
|
|
||||||
|
|
||||||
def get_filters(self, search_global, until_position=None, origin_scope=None):
|
|
||||||
yield self.function_execution_filter(self.evaluator, self,
|
|
||||||
until_position=until_position,
|
|
||||||
origin_scope=origin_scope)
|
|
||||||
|
|
||||||
@evaluator_method_cache()
|
|
||||||
def get_params(self):
|
|
||||||
return self.var_args.get_params(self)
|
|
||||||
|
|
||||||
|
|
||||||
class ImplicitNSName(AbstractNameDefinition):
|
class ImplicitNSName(AbstractNameDefinition):
|
||||||
"""
|
"""
|
||||||
Accessing names for implicit namespace packages should infer to nothing.
|
Accessing names for implicit namespace packages should infer to nothing.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from jedi.evaluate import iterable
|
|||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
from jedi.evaluate import representation as er
|
from jedi.evaluate import representation as er
|
||||||
|
from jedi.evaluate.context.function import FunctionContext
|
||||||
from jedi.evaluate.context.instance import TreeInstance, CompiledInstance
|
from jedi.evaluate.context.instance import TreeInstance, CompiledInstance
|
||||||
from jedi.evaluate.finder import NameFinder
|
from jedi.evaluate.finder import NameFinder
|
||||||
from jedi.evaluate.helpers import is_string, is_literal, is_number, is_compiled
|
from jedi.evaluate.helpers import is_string, is_literal, is_number, is_compiled
|
||||||
@@ -63,7 +64,7 @@ def eval_node(context, element):
|
|||||||
# else: print e.g. could be evaluated like this in Python 2.7
|
# else: print e.g. could be evaluated like this in Python 2.7
|
||||||
return NO_CONTEXTS
|
return NO_CONTEXTS
|
||||||
elif typ == 'lambdef':
|
elif typ == 'lambdef':
|
||||||
return ContextSet(er.FunctionContext(evaluator, context, element))
|
return ContextSet(FunctionContext(evaluator, context, element))
|
||||||
elif typ == 'expr_stmt':
|
elif typ == 'expr_stmt':
|
||||||
return eval_expr_stmt(context, element)
|
return eval_expr_stmt(context, element)
|
||||||
elif typ in ('power', 'atom_expr'):
|
elif typ in ('power', 'atom_expr'):
|
||||||
@@ -508,7 +509,7 @@ def _apply_decorators(context, node):
|
|||||||
classdef=node
|
classdef=node
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
decoratee_context = er.FunctionContext(
|
decoratee_context = FunctionContext(
|
||||||
context.evaluator,
|
context.evaluator,
|
||||||
parent_context=context,
|
parent_context=context,
|
||||||
funcdef=node
|
funcdef=node
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import parso
|
|||||||
from jedi._compatibility import builtins, is_py3
|
from jedi._compatibility import builtins, is_py3
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate.context import instance
|
from jedi.evaluate.context import instance
|
||||||
from jedi.evaluate.representation import FunctionContext
|
from jedi.evaluate.context.function import FunctionContext
|
||||||
from jedi.evaluate import Evaluator
|
from jedi.evaluate import Evaluator
|
||||||
from jedi.parser_utils import clean_scope_docstring
|
from jedi.parser_utils import clean_scope_docstring
|
||||||
from jedi import Script
|
from jedi import Script
|
||||||
|
|||||||
Reference in New Issue
Block a user