Quite a few changes to prepare arrays

This commit is contained in:
Dave Halter
2018-09-18 00:17:51 +02:00
parent 75ab83da63
commit 1b11162132
7 changed files with 116 additions and 77 deletions

View File

@@ -16,7 +16,6 @@ from jedi.evaluate.base_context import ContextualizedNode, NO_CONTEXTS, \
ContextSet, TreeContext, ContextWrapper ContextSet, TreeContext, ContextWrapper
from jedi.evaluate.lazy_context import LazyKnownContexts, LazyKnownContext, \ from jedi.evaluate.lazy_context import LazyKnownContexts, LazyKnownContext, \
LazyTreeContext LazyTreeContext
from jedi.evaluate.context.typing import TypeVar
from jedi.evaluate.context import iterable from jedi.evaluate.context import iterable
from jedi.evaluate.context import asynchronous from jedi.evaluate.context import asynchronous
from jedi import parser_utils from jedi import parser_utils
@@ -127,6 +126,9 @@ class FunctionContext(use_metaclass(CachedMetaClass, AbstractFunction)):
def get_default_param_context(self): def get_default_param_context(self):
return self.parent_context return self.parent_context
def get_matching_functions(self, arguments):
yield self
class MethodContext(FunctionContext): class MethodContext(FunctionContext):
def __init__(self, evaluator, class_context, *args, **kwargs): def __init__(self, evaluator, class_context, *args, **kwargs):
@@ -302,18 +304,22 @@ class OverloadedFunctionContext(ContextWrapper):
self._overloaded_functions = overloaded_functions self._overloaded_functions = overloaded_functions
def py__call__(self, arguments): def py__call__(self, arguments):
context_set = ContextSet()
debug.dbg("Execute overloaded function %s", self._wrapped_context, color='BLUE') debug.dbg("Execute overloaded function %s", self._wrapped_context, color='BLUE')
return ContextSet.from_sets(
matching_function.py__call__(arguments=arguments)
for matching_function in self.get_matching_functions(arguments)
)
def get_matching_functions(self, arguments):
for f in self._overloaded_functions: for f in self._overloaded_functions:
signature = parser_utils.get_call_signature(f.tree_node) signature = parser_utils.get_call_signature(f.tree_node)
if signature_matches(f, arguments): if signature_matches(f, arguments):
debug.dbg("Overloading match: %s@%s", debug.dbg("Overloading match: %s@%s",
signature, f.tree_node.start_pos[0], color='BLUE') signature, f.tree_node.start_pos[0], color='BLUE')
context_set |= f.py__call__(arguments=arguments) yield f
else: else:
debug.dbg("Overloading no match: %s@%s (%s)", debug.dbg("Overloading no match: %s@%s (%s)",
signature, f.tree_node.start_pos[0], arguments, color='BLUE') signature, f.tree_node.start_pos[0], arguments, color='BLUE')
return context_set
def signature_matches(function_context, arguments): def signature_matches(function_context, arguments):
@@ -369,11 +375,17 @@ def _find_overload_functions(context, tree_node):
until_position=tree_node.start_pos until_position=tree_node.start_pos
) )
names = filter.get(tree_node.name.value) names = filter.get(tree_node.name.value)
assert isinstance(names, list)
if not names:
break
found = False
for name in names: for name in names:
funcdef = name.tree_name.parent funcdef = name.tree_name.parent
if funcdef.type == 'funcdef' and _is_overload_decorated(funcdef): if funcdef.type == 'funcdef' and _is_overload_decorated(funcdef):
tree_node = funcdef
found = True
yield funcdef yield funcdef
# TODO this is probably not good enough? Why are we always breaking? if not found:
break # By default break break

View File

@@ -358,6 +358,13 @@ class BoundMethod(AbstractFunction):
function_execution = self.get_function_execution(arguments) function_execution = self.get_function_execution(arguments)
return function_execution.infer() return function_execution.infer()
def get_matching_functions(self, arguments):
for func in self._function.get_matching_functions(arguments):
if func is self:
yield self
else:
yield BoundMethod(self.instance, self.class_context, func)
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self._function) return '<%s: %s>' % (self.__class__.__name__, self._function)

View File

@@ -32,13 +32,12 @@ from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts, \
from jedi.evaluate.helpers import get_int_or_none, is_string, \ from jedi.evaluate.helpers import get_int_or_none, is_string, \
predefine_names, evaluate_call_of_leaf, reraise_getitem_errors, \ predefine_names, evaluate_call_of_leaf, reraise_getitem_errors, \
SimpleGetItemNotFound SimpleGetItemNotFound
from jedi.evaluate.utils import safe_property from jedi.evaluate.utils import safe_property, to_list
from jedi.evaluate.utils import to_list
from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.cache import evaluator_method_cache
from jedi.evaluate.helpers import execute_evaluated from jedi.evaluate.helpers import execute_evaluated
from jedi.evaluate.filters import ParserTreeFilter, BuiltinOverwrite, \ from jedi.evaluate.filters import ParserTreeFilter, BuiltinOverwrite, \
publish_method publish_method
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context, \ from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, \
TreeContext, ContextualizedNode, iterate_contexts TreeContext, ContextualizedNode, iterate_contexts
from jedi.parser_utils import get_comp_fors from jedi.parser_utils import get_comp_fors
@@ -188,9 +187,37 @@ class Sequence(BuiltinOverwrite, IterableMixin):
@memoize_method @memoize_method
def get_object(self): def get_object(self):
compiled_obj = compiled.builtin_from_name(self.evaluator, self.array_type) klass = compiled.builtin_from_name(self.evaluator, self.array_type)
only_obj, = execute_evaluated(compiled_obj, self) return self._annotate_class(klass)
return only_obj
def _annotate_class(self, klass):
from jedi.evaluate.pep0484 import define_type_vars_for_execution
instance, = klass.execute_evaluated(self)
for init_function in self._get_init_functions(instance):
# Just take the first result, it should always be one, because we
# control the typeshed code.
execution_context = init_function.get_function_execution()
return define_type_vars_for_execution(
ContextSet(klass),
execution_context,
klass.find_annotation_variables()
)
return instance
assert "Should never land here, probably an issue with typeshed changes"
def _get_init_functions(self, instance):
from jedi.evaluate.context.function import OverloadedFunctionContext
from jedi.evaluate import arguments
for init in instance.py__getattribute__('__init__'):
try:
method = init.get_matching_functions
except AttributeError:
continue
else:
for x in method(arguments.ValuesArguments([ContextSet(self)])):
yield x
def py__bool__(self): def py__bool__(self):
return None # We don't know the length, because of appends. return None # We don't know the length, because of appends.

View File

@@ -45,6 +45,7 @@ from jedi.evaluate import compiled
from jedi.evaluate.lazy_context import LazyKnownContext from jedi.evaluate.lazy_context import LazyKnownContext
from jedi.evaluate.filters import ParserTreeFilter, TreeNameDefinition, \ from jedi.evaluate.filters import ParserTreeFilter, TreeNameDefinition, \
ContextName ContextName
from jedi.evaluate.arguments import unpack_arglist
from jedi.evaluate.base_context import ContextSet, iterator_to_context_set, \ from jedi.evaluate.base_context import ContextSet, iterator_to_context_set, \
TreeContext TreeContext
@@ -166,6 +167,21 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)):
""" """
api_type = u'class' api_type = u'class'
@evaluator_method_cache()
def find_annotation_variables(self):
found = []
arglist = self.tree_node.get_super_arglist()
if arglist is None:
return []
for stars, node in unpack_arglist(arglist):
if stars:
continue # These are not relevant for this search.
from jedi.evaluate.pep0484 import find_unknown_type_vars
found += find_unknown_type_vars(self.parent_context, node)
return found
@evaluator_method_cache(default=()) @evaluator_method_cache(default=())
def py__bases__(self): def py__bases__(self):
arglist = self.tree_node.get_super_arglist() arglist = self.tree_node.get_super_arglist()

View File

@@ -11,7 +11,7 @@ from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context, \
iterator_to_context_set, HelperContextMixin iterator_to_context_set, HelperContextMixin
from jedi.evaluate.lazy_context import LazyKnownContexts, LazyKnownContext from jedi.evaluate.lazy_context import LazyKnownContexts, LazyKnownContext
from jedi.evaluate.context.iterable import SequenceLiteralContext from jedi.evaluate.context.iterable import SequenceLiteralContext
from jedi.evaluate.arguments import repack_with_argument_clinic, unpack_arglist from jedi.evaluate.arguments import repack_with_argument_clinic
from jedi.evaluate.utils import to_list from jedi.evaluate.utils import to_list
from jedi.evaluate.filters import FilterWrapper, NameWrapper, \ from jedi.evaluate.filters import FilterWrapper, NameWrapper, \
AbstractTreeName, AbstractNameDefinition, ContextName AbstractTreeName, AbstractNameDefinition, ContextName
@@ -499,36 +499,6 @@ class _AbstractAnnotatedClass(ClassContext):
# not a direct lookup on the class. # not a direct lookup on the class.
yield self.get_type_var_filter() yield self.get_type_var_filter()
@evaluator_method_cache()
def find_annotation_variables(self):
found = []
arglist = self.tree_node.get_super_arglist()
if arglist is None:
return []
for stars, node in unpack_arglist(arglist):
if stars:
continue # These are not relevant for this search.
if node.type == 'atom_expr':
trailer = node.children[-1]
if trailer.type == 'trailer' and trailer.children[0] == '[':
for subscript_node in self._unpack_subscriptlist(trailer.children[1]):
type_var_set = self.parent_context.eval_node(subscript_node)
for type_var in type_var_set:
if isinstance(type_var, TypeVar) and type_var not in found:
found.append(type_var)
return found
def _unpack_subscriptlist(self, subscriptlist):
if subscriptlist.type == 'subscriptlist':
for subscript in subscriptlist.children[::2]:
if subscript.type != 'subscript':
yield subscript
else:
if subscriptlist.type != 'subscript':
yield subscriptlist
def is_same_class(self, other): def is_same_class(self, other):
if not isinstance(other, _AbstractAnnotatedClass): if not isinstance(other, _AbstractAnnotatedClass):
return False return False

View File

@@ -5,7 +5,6 @@ from jedi.evaluate import analysis
from jedi.evaluate.lazy_context import LazyKnownContext, \ from jedi.evaluate.lazy_context import LazyKnownContext, \
LazyTreeContext, LazyUnknownContext LazyTreeContext, LazyUnknownContext
from jedi.evaluate import docstrings from jedi.evaluate import docstrings
from jedi.evaluate import pep0484
from jedi.evaluate.context import iterable from jedi.evaluate.context import iterable
@@ -27,6 +26,7 @@ class ExecutedParam(object):
def infer(self, use_hints=True): def infer(self, use_hints=True):
if use_hints: if use_hints:
from jedi.evaluate import pep0484
pep0484_hints = pep0484.infer_param(self._execution_context, self._param_node) pep0484_hints = pep0484.infer_param(self._execution_context, self._param_node)
doc_params = docstrings.infer_param(self._execution_context, self._param_node) doc_params = docstrings.infer_param(self._execution_context, self._param_node)
if pep0484_hints or doc_params: if pep0484_hints or doc_params:

View File

@@ -29,8 +29,7 @@ from parso.python import tree
from jedi._compatibility import unicode, force_unicode from jedi._compatibility import unicode, force_unicode
from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.cache import evaluator_method_cache
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate.base_context import NO_CONTEXTS, ContextSet, ContextWrapper from jedi.evaluate.base_context import NO_CONTEXTS, ContextSet
from jedi.evaluate.filters import DictFilter
from jedi.evaluate.lazy_context import LazyTreeContext from jedi.evaluate.lazy_context import LazyTreeContext
from jedi.evaluate.context import ModuleContext from jedi.evaluate.context import ModuleContext
from jedi.evaluate.context.typing import TypeVar, AnnotatedClass, AnnotatedSubClass from jedi.evaluate.context.typing import TypeVar, AnnotatedClass, AnnotatedSubClass
@@ -214,14 +213,30 @@ def infer_return_types(function_execution_context):
function_execution_context.function_context.get_default_param_context(), function_execution_context.function_context.get_default_param_context(),
match.group(1).strip() match.group(1).strip()
) )
if annotation is None:
return NO_CONTEXTS
context = function_execution_context.function_context.get_default_param_context() context = function_execution_context.function_context.get_default_param_context()
return _define_type_vars( unknown_type_vars = list(find_unknown_type_vars(context, annotation))
if not unknown_type_vars:
return context.eval_node(annotation)
return define_type_vars_for_execution(
context.eval_node(annotation), context.eval_node(annotation),
_infer_type_vars(function_execution_context, all_annotations), function_execution_context,
unknown_type_vars,
).execute_annotation() ).execute_annotation()
def define_type_vars_for_execution(to_define_contexts, execution_context,
unknown_type_vars):
all_annotations = py__annotations__(execution_context.tree_node)
return _define_type_vars(
to_define_contexts,
_infer_type_vars(execution_context, all_annotations),
)
def _infer_type_vars(execution_context, annotation_dict): def _infer_type_vars(execution_context, annotation_dict):
""" """
Some functions use type vars that are not defined by the class, but rather Some functions use type vars that are not defined by the class, but rather
@@ -233,32 +248,19 @@ def _infer_type_vars(execution_context, annotation_dict):
3. Return the union of all type vars that have been found. 3. Return the union of all type vars that have been found.
""" """
context = execution_context.function_context.get_default_param_context() context = execution_context.function_context.get_default_param_context()
try:
return_annotation = annotation_dict['return']
except KeyError:
return {}
unknown_type_vars = list(find_annotation_variables(context, return_annotation))
if not unknown_type_vars:
return {}
executed_params = execution_context.get_executed_params()
annotation_variable_results = {} annotation_variable_results = {}
# The annotation_dict is ordered. for executed_param in execution_context.get_executed_params():
for i, (annotation_name, annotation_node) in enumerate(annotation_dict.items()): try:
if annotation_name == 'return': annotation_node = annotation_dict[executed_param.string_name]
except KeyError:
continue continue
annotation_variables = find_annotation_variables(context, annotation_node) annotation_variables = find_unknown_type_vars(context, annotation_node)
if annotation_variables: if annotation_variables:
try:
param = executed_params[i]
except IndexError:
continue
# Infer unknown type var # Infer unknown type var
annotation_context_set = context.eval_node(annotation_node) annotation_context_set = context.eval_node(annotation_node)
actual_context_set = param.infer(use_hints=False) actual_context_set = executed_param.infer(use_hints=False)
for ann in annotation_context_set: for ann in annotation_context_set:
_merge_type_var_dicts( _merge_type_var_dicts(
annotation_variable_results, annotation_variable_results,
@@ -444,16 +446,21 @@ def _find_type_from_comment_hint(context, node, varlist, name):
return _evaluate_annotation_string(context, match.group(1).strip(), index) return _evaluate_annotation_string(context, match.group(1).strip(), index)
def find_annotation_variables(context, node): def find_unknown_type_vars(context, node):
found = [] def check_node(node):
if node.type == 'atom_expr': if node.type == 'atom_expr':
trailer = node.children[-1] trailer = node.children[-1]
if trailer.type == 'trailer' and trailer.children[0] == '[': if trailer.type == 'trailer' and trailer.children[0] == '[':
for subscript_node in _unpack_subscriptlist(trailer.children[1]): for subscript_node in _unpack_subscriptlist(trailer.children[1]):
type_var_set = context.eval_node(subscript_node) check_node(subscript_node)
for type_var in type_var_set: else:
if isinstance(type_var, TypeVar) and type_var not in found: type_var_set = context.eval_node(node)
found.append(type_var) for type_var in type_var_set:
if isinstance(type_var, TypeVar):
found.add(type_var)
found = set()
check_node(node)
return found return found