mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Start putting the signature matching onto the ExecutedParam class
This commit is contained in:
@@ -110,8 +110,10 @@ class FunctionContext(use_metaclass(CachedMetaClass, AbstractFunction)):
|
|||||||
)
|
)
|
||||||
return function
|
return function
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments, need_param_match=False):
|
||||||
function_execution = self.get_function_execution(arguments)
|
function_execution = self.get_function_execution(arguments)
|
||||||
|
if need_param_match and function_execution.matches_signature():
|
||||||
|
return NO_CONTEXTS
|
||||||
return function_execution.infer()
|
return function_execution.infer()
|
||||||
|
|
||||||
def get_function_execution(self, arguments=None):
|
def get_function_execution(self, arguments=None):
|
||||||
@@ -274,6 +276,19 @@ class FunctionExecutionContext(TreeContext):
|
|||||||
def get_executed_params(self):
|
def get_executed_params(self):
|
||||||
return self.var_args.get_executed_params(self)
|
return self.var_args.get_executed_params(self)
|
||||||
|
|
||||||
|
def matches_signature(self):
|
||||||
|
matches = all(executed_param.matches_signature()
|
||||||
|
for executed_param in self.get_executed_params())
|
||||||
|
if debug.enable_notice:
|
||||||
|
signature = parser_utils.get_call_signature(self.tree_node)
|
||||||
|
if matches:
|
||||||
|
debug.dbg("Overloading match: %s@%s",
|
||||||
|
signature, self.tree_node.start_pos[0], color='BLUE')
|
||||||
|
else:
|
||||||
|
debug.dbg("Overloading no match: %s@%s (%s)",
|
||||||
|
signature, self.tree_node.start_pos[0], self.var_args, color='BLUE')
|
||||||
|
return matches
|
||||||
|
|
||||||
def infer(self):
|
def infer(self):
|
||||||
"""
|
"""
|
||||||
Created to be used by inheritance.
|
Created to be used by inheritance.
|
||||||
@@ -301,17 +316,17 @@ class FunctionExecutionContext(TreeContext):
|
|||||||
class OverloadedFunctionContext(ContextWrapper):
|
class OverloadedFunctionContext(ContextWrapper):
|
||||||
def __init__(self, function, overloaded_functions):
|
def __init__(self, function, overloaded_functions):
|
||||||
super(OverloadedFunctionContext, self).__init__(function)
|
super(OverloadedFunctionContext, self).__init__(function)
|
||||||
self._overloaded_functions = overloaded_functions
|
self.overloaded_functions = overloaded_functions
|
||||||
|
|
||||||
def py__call__(self, arguments):
|
def py__call__(self, arguments):
|
||||||
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(
|
return ContextSet.from_sets(
|
||||||
matching_function.py__call__(arguments=arguments)
|
f.py__call__(arguments=arguments, need_param_match=True)
|
||||||
for matching_function in self.get_matching_functions(arguments)
|
for f in self.overloaded_functions
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_matching_functions(self, 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",
|
||||||
@@ -324,15 +339,18 @@ class OverloadedFunctionContext(ContextWrapper):
|
|||||||
|
|
||||||
def signature_matches(function_context, arguments):
|
def signature_matches(function_context, arguments):
|
||||||
unpacked_arguments = arguments.unpack()
|
unpacked_arguments = arguments.unpack()
|
||||||
|
key_args = {}
|
||||||
for param_node in function_context.tree_node.get_params():
|
for param_node in function_context.tree_node.get_params():
|
||||||
|
while True:
|
||||||
key, argument = next(unpacked_arguments, (None, None))
|
key, argument = next(unpacked_arguments, (None, None))
|
||||||
|
if key is None or argument is None:
|
||||||
|
break
|
||||||
|
key_args[key] = argument
|
||||||
|
if argument is None:
|
||||||
|
argument = key_args.pop(param_node.name.value, None)
|
||||||
if argument is None:
|
if argument is None:
|
||||||
# This signature has an parameter more than arguments were given.
|
# This signature has an parameter more than arguments were given.
|
||||||
return bool(param_node.star_count)
|
return bool(param_node.star_count == 1)
|
||||||
if key is not None:
|
|
||||||
# TODO this is obviously wrong, we cannot just ignore keyword
|
|
||||||
# arguments, but it's easier for now.
|
|
||||||
return False
|
|
||||||
|
|
||||||
if param_node.annotation is not None:
|
if param_node.annotation is not None:
|
||||||
if param_node.star_count == 2:
|
if param_node.star_count == 2:
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ class AbstractInstanceContext(Context):
|
|||||||
def is_class(self):
|
def is_class(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_annotated_class_object(self):
|
||||||
|
return self.class_context # This is the default.
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def py__call__(self):
|
def py__call__(self):
|
||||||
names = self.get_function_slot_names(u'__call__')
|
names = self.get_function_slot_names(u'__call__')
|
||||||
@@ -118,8 +121,9 @@ class AbstractInstanceContext(Context):
|
|||||||
|
|
||||||
def get_filters(self, search_global=None, until_position=None,
|
def get_filters(self, search_global=None, until_position=None,
|
||||||
origin_scope=None, include_self_names=True):
|
origin_scope=None, include_self_names=True):
|
||||||
|
class_context = self.get_annotated_class_object()
|
||||||
if include_self_names:
|
if include_self_names:
|
||||||
for cls in py__mro__(self.class_context):
|
for cls in py__mro__(class_context):
|
||||||
if not isinstance(cls, compiled.CompiledObject) \
|
if not isinstance(cls, compiled.CompiledObject) \
|
||||||
or cls.tree_node is not None:
|
or cls.tree_node is not None:
|
||||||
# In this case we're excluding compiled objects that are
|
# In this case we're excluding compiled objects that are
|
||||||
@@ -127,7 +131,7 @@ class AbstractInstanceContext(Context):
|
|||||||
# compiled objects to search for self variables.
|
# compiled objects to search for self variables.
|
||||||
yield SelfAttributeFilter(self.evaluator, self, cls, origin_scope)
|
yield SelfAttributeFilter(self.evaluator, self, cls, origin_scope)
|
||||||
|
|
||||||
for cls in py__mro__(self.class_context):
|
for cls in py__mro__(class_context):
|
||||||
if isinstance(cls, compiled.CompiledObject):
|
if isinstance(cls, compiled.CompiledObject):
|
||||||
yield CompiledInstanceClassFilter(self.evaluator, self, cls)
|
yield CompiledInstanceClassFilter(self.evaluator, self, cls)
|
||||||
else:
|
else:
|
||||||
@@ -170,18 +174,16 @@ class AbstractInstanceContext(Context):
|
|||||||
def name(self):
|
def name(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _create_init_execution(self, class_context, bound_method):
|
|
||||||
return bound_method.get_function_execution(self.var_args)
|
|
||||||
|
|
||||||
def create_init_executions(self):
|
def create_init_executions(self):
|
||||||
for name in self.get_function_slot_names(u'__init__'):
|
for name in self.get_function_slot_names(u'__init__'):
|
||||||
|
# TODO is this correct? I think we need to check for functions.
|
||||||
if isinstance(name, LazyInstanceClassName):
|
if isinstance(name, LazyInstanceClassName):
|
||||||
function = FunctionContext.from_context(
|
function = FunctionContext.from_context(
|
||||||
self.parent_context,
|
self.parent_context,
|
||||||
name.tree_name.parent
|
name.tree_name.parent
|
||||||
)
|
)
|
||||||
bound_method = BoundMethod(self, name.class_context, function)
|
bound_method = BoundMethod(self, name.class_context, function)
|
||||||
yield self._create_init_execution(name.class_context, bound_method)
|
yield bound_method.get_function_execution(self.var_args)
|
||||||
|
|
||||||
@evaluator_method_cache()
|
@evaluator_method_cache()
|
||||||
def create_instance_context(self, class_context, node):
|
def create_instance_context(self, class_context, node):
|
||||||
@@ -199,7 +201,7 @@ class AbstractInstanceContext(Context):
|
|||||||
)
|
)
|
||||||
bound_method = BoundMethod(self, class_context, func)
|
bound_method = BoundMethod(self, class_context, func)
|
||||||
if scope.name.value == '__init__' and parent_context == class_context:
|
if scope.name.value == '__init__' and parent_context == class_context:
|
||||||
return self._create_init_execution(class_context, bound_method)
|
return bound_method.get_function_execution(self.var_args)
|
||||||
else:
|
else:
|
||||||
return bound_method.get_function_execution()
|
return bound_method.get_function_execution()
|
||||||
elif scope.type == 'classdef':
|
elif scope.type == 'classdef':
|
||||||
@@ -259,6 +261,38 @@ class TreeInstance(AbstractInstanceContext):
|
|||||||
def name(self):
|
def name(self):
|
||||||
return filters.ContextName(self, self.class_context.name.tree_name)
|
return filters.ContextName(self, self.class_context.name.tree_name)
|
||||||
|
|
||||||
|
@evaluator_method_cache()
|
||||||
|
def get_annotated_class_object(self):
|
||||||
|
from jedi.evaluate.pep0484 import define_type_vars_for_execution
|
||||||
|
|
||||||
|
for func in self._get_annotation_init_functions():
|
||||||
|
# Just take the first result, it should always be one, because we
|
||||||
|
# control the typeshed code.
|
||||||
|
bound = BoundMethod(self, self.class_context, func)
|
||||||
|
execution = bound.get_function_execution(self.var_args)
|
||||||
|
if not execution.matches_signature():
|
||||||
|
# First check if the signature even matches, if not we don't
|
||||||
|
# need to infer anything.
|
||||||
|
print('no m', bound)
|
||||||
|
continue
|
||||||
|
print(bound)
|
||||||
|
context_set = define_type_vars_for_execution(
|
||||||
|
ContextSet(self.class_context),
|
||||||
|
execution,
|
||||||
|
self.class_context.list_type_vars()
|
||||||
|
)
|
||||||
|
if context_set:
|
||||||
|
return next(iter(context_set))
|
||||||
|
return self.class_context
|
||||||
|
|
||||||
|
def _get_annotation_init_functions(self):
|
||||||
|
for init in self.class_context.py__getattribute__('__init__'):
|
||||||
|
if isinstance(init, OverloadedFunctionContext):
|
||||||
|
for func in init.overloaded_functions:
|
||||||
|
yield func
|
||||||
|
elif isinstance(init, FunctionContext):
|
||||||
|
yield init
|
||||||
|
|
||||||
|
|
||||||
class AnonymousInstance(TreeInstance):
|
class AnonymousInstance(TreeInstance):
|
||||||
def __init__(self, evaluator, parent_context, class_context):
|
def __init__(self, evaluator, parent_context, class_context):
|
||||||
|
|||||||
@@ -188,37 +188,8 @@ class Sequence(BuiltinOverwrite, IterableMixin):
|
|||||||
@memoize_method
|
@memoize_method
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
klass = compiled.builtin_from_name(self.evaluator, self.array_type)
|
klass = compiled.builtin_from_name(self.evaluator, self.array_type)
|
||||||
annotated_instance, = self._annotate_class(klass).execute_evaluated()
|
instance, = klass.execute_evaluated(self)
|
||||||
return annotated_instance
|
return instance
|
||||||
|
|
||||||
def _annotate_class(self, klass):
|
|
||||||
from jedi.evaluate.pep0484 import define_type_vars_for_execution
|
|
||||||
|
|
||||||
instance, = klass.execute(self)
|
|
||||||
|
|
||||||
for execution_context in self._get_init_executions(instance):
|
|
||||||
# Just take the first result, it should always be one, because we
|
|
||||||
# control the typeshed code.
|
|
||||||
return define_type_vars_for_execution(
|
|
||||||
ContextSet(klass),
|
|
||||||
execution_context,
|
|
||||||
klass.list_type_vars()
|
|
||||||
)
|
|
||||||
assert "Should never land here, probably an issue with typeshed changes"
|
|
||||||
|
|
||||||
def _get_init_executions(self, instance):
|
|
||||||
from jedi.evaluate import arguments
|
|
||||||
from jedi.evaluate.context.instance import InstanceArguments
|
|
||||||
for init in instance.py__getattribute__('__init__'):
|
|
||||||
try:
|
|
||||||
method = init.get_matching_functions
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
base_args = arguments.ValuesArguments([ContextSet(self)])
|
|
||||||
arguments = InstanceArguments(instance, base_args)
|
|
||||||
for func in method(arguments):
|
|
||||||
yield func.get_function_execution(base_args)
|
|
||||||
|
|
||||||
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.
|
||||||
|
|||||||
@@ -24,16 +24,32 @@ class ExecutedParam(object):
|
|||||||
self._lazy_context = lazy_context
|
self._lazy_context = lazy_context
|
||||||
self.string_name = param_node.name.value
|
self.string_name = param_node.name.value
|
||||||
|
|
||||||
|
def infer_annotations(self):
|
||||||
|
from jedi.evaluate import pep0484
|
||||||
|
return pep0484.infer_param(self._execution_context, self._param_node)
|
||||||
|
|
||||||
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)
|
|
||||||
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:
|
ann = self.infer_annotations().execute_annotation()
|
||||||
return pep0484_hints | doc_params
|
if ann or doc_params:
|
||||||
|
return ann | doc_params
|
||||||
|
|
||||||
return self._lazy_context.infer()
|
return self._lazy_context.infer()
|
||||||
|
|
||||||
|
def matches_signature(self):
|
||||||
|
argument_contexts = self.infer(use_hints=False).py__class__()
|
||||||
|
if self._param_node.star_count:
|
||||||
|
return True
|
||||||
|
annotations = self.infer_annotations()
|
||||||
|
if not annotations:
|
||||||
|
# If we cannot infer annotations - or there aren't any - pretend
|
||||||
|
# that the signature matches.
|
||||||
|
return True
|
||||||
|
return any(c1.is_sub_class_of(c2)
|
||||||
|
for c1 in argument_contexts
|
||||||
|
for c2 in annotations)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def var_args(self):
|
def var_args(self):
|
||||||
return self._execution_context.var_args
|
return self._execution_context.var_args
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def _evaluate_annotation_string(context, string, index=None):
|
|||||||
lambda context: context.array_type == u'tuple' # noqa
|
lambda context: context.array_type == u'tuple' # noqa
|
||||||
and len(list(context.py__iter__())) >= index
|
and len(list(context.py__iter__())) >= index
|
||||||
).py__simple_getitem__(index)
|
).py__simple_getitem__(index)
|
||||||
return context_set.execute_annotation()
|
return context_set
|
||||||
|
|
||||||
|
|
||||||
def _fix_forward_reference(context, node):
|
def _fix_forward_reference(context, node):
|
||||||
@@ -174,7 +174,7 @@ def infer_param(execution_context, param):
|
|||||||
)
|
)
|
||||||
# Annotations are like default params and resolve in the same way.
|
# Annotations are like default params and resolve in the same way.
|
||||||
context = execution_context.function_context.get_default_param_context()
|
context = execution_context.function_context.get_default_param_context()
|
||||||
return _evaluate_for_annotation(context, annotation).execute_annotation()
|
return _evaluate_for_annotation(context, annotation)
|
||||||
|
|
||||||
|
|
||||||
def py__annotations__(funcdef):
|
def py__annotations__(funcdef):
|
||||||
@@ -212,7 +212,7 @@ def infer_return_types(function_execution_context):
|
|||||||
return _evaluate_annotation_string(
|
return _evaluate_annotation_string(
|
||||||
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()
|
||||||
)
|
).execute_annotation()
|
||||||
if annotation is None:
|
if annotation is None:
|
||||||
return NO_CONTEXTS
|
return NO_CONTEXTS
|
||||||
|
|
||||||
@@ -255,6 +255,8 @@ def _infer_type_vars_for_execution(execution_context, annotation_dict):
|
|||||||
annotation_node = annotation_dict[executed_param.string_name]
|
annotation_node = annotation_dict[executed_param.string_name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
|
if executed_param._param_node.star_count: # TODO remove this.
|
||||||
|
continue
|
||||||
|
|
||||||
annotation_variables = find_unknown_type_vars(context, annotation_node)
|
annotation_variables = find_unknown_type_vars(context, annotation_node)
|
||||||
if annotation_variables:
|
if annotation_variables:
|
||||||
@@ -478,7 +480,9 @@ def _find_type_from_comment_hint(context, node, varlist, name):
|
|||||||
match = re.match(r"^#\s*type:\s*([^#]*)", comment)
|
match = re.match(r"^#\s*type:\s*([^#]*)", comment)
|
||||||
if match is None:
|
if match is None:
|
||||||
return []
|
return []
|
||||||
return _evaluate_annotation_string(context, match.group(1).strip(), index)
|
return _evaluate_annotation_string(
|
||||||
|
context, match.group(1).strip(), index
|
||||||
|
).execute_annotation()
|
||||||
|
|
||||||
|
|
||||||
def find_unknown_type_vars(context, node):
|
def find_unknown_type_vars(context, node):
|
||||||
|
|||||||
@@ -209,7 +209,6 @@ g
|
|||||||
dic2 = {'asdf': 3, 'b': 'str'}
|
dic2 = {'asdf': 3, 'b': 'str'}
|
||||||
#? int()
|
#? int()
|
||||||
dic2['asdf']
|
dic2['asdf']
|
||||||
# TODO for now get doesn't work properly when used with a literal.
|
|
||||||
#? None int() str()
|
#? None int() str()
|
||||||
dic2.get('asdf')
|
dic2.get('asdf')
|
||||||
|
|
||||||
@@ -268,8 +267,12 @@ for x in {1: 3.0, '': 1j}:
|
|||||||
dict().values().__iter__
|
dict().values().__iter__
|
||||||
|
|
||||||
d = dict(a=3, b='')
|
d = dict(a=3, b='')
|
||||||
#? int() str()
|
# Indexing is not supported
|
||||||
|
#?
|
||||||
d.values()[0]
|
d.values()[0]
|
||||||
|
x, = d.values()
|
||||||
|
#? int() str()
|
||||||
|
x
|
||||||
#? int()
|
#? int()
|
||||||
d['a']
|
d['a']
|
||||||
#? int() None
|
#? int() None
|
||||||
|
|||||||
Reference in New Issue
Block a user