diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index 7e49f413..279a25db 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -103,12 +103,11 @@ class MethodExecutionContext(FunctionExecutionContext): class AbstractInstanceValue(Value): api_type = u'instance' - def __init__(self, inference_state, parent_context, class_value, arguments): + def __init__(self, inference_state, parent_context, class_value): super(AbstractInstanceValue, self).__init__(inference_state, parent_context) # Generated instances are classes that are just generated by self # (No arguments) used. self.class_value = class_value - self.arguments = arguments def is_instance(self): return True @@ -135,14 +134,14 @@ class AbstractInstanceValue(Value): return [s.bind(self) for s in call_funcs.get_signatures()] def __repr__(self): - return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_value) + return "<%s of %s>" % (self.__class__.__name__, self.class_value) class CompiledInstance(AbstractInstanceValue): def __init__(self, inference_state, parent_context, class_value, arguments): - self._original_arguments = arguments super(CompiledInstance, self).__init__(inference_state, parent_context, - class_value, arguments) + class_value) + self.arguments = arguments def get_filters(self, origin_scope=None, include_self_names=True): class_value = self.get_annotated_class_object() @@ -158,7 +157,7 @@ class CompiledInstance(AbstractInstanceValue): return compiled.CompiledValueName(self, self.class_value.name.string_name) def get_first_non_keyword_argument_values(self): - key, lazy_value = next(self._original_arguments.unpack(), ('', None)) + key, lazy_value = next(self.arguments.unpack(), ('', None)) if key is not None: return NO_VALUES @@ -168,20 +167,7 @@ class CompiledInstance(AbstractInstanceValue): return False -class TreeInstance(AbstractInstanceValue): - def __init__(self, inference_state, parent_context, class_value, arguments): - # I don't think that dynamic append lookups should happen here. That - # sounds more like something that should go to py__iter__. - if class_value.py__name__() in ['list', 'set'] \ - and parent_context.get_root_context().is_builtins_module(): - # compare the module path with the builtin name. - if settings.dynamic_array_additions: - arguments = get_dynamic_array_instance(self, arguments) - - super(TreeInstance, self).__init__(inference_state, parent_context, - class_value, arguments) - self.tree_node = class_value.tree_node - +class _BaseTreeInstance(AbstractInstanceValue): @property def array_type(self): name = self.class_value.py__name__() @@ -194,35 +180,6 @@ class TreeInstance(AbstractInstanceValue): def name(self): return ValueName(self, self.class_value.name.tree_name) - # This can recurse, if the initialization of the class includes a reference - # to itself. - @inference_state_method_cache(default=None) - def _get_annotated_class_object(self): - from jedi.inference.gradual.annotation import py__annotations__, \ - infer_type_vars_for_execution - - args = InstanceArguments(self, self.arguments) - for signature in self.class_value.py__getattribute__('__init__').get_signatures(): - # Just take the first result, it should always be one, because we - # control the typeshed code. - if not signature.matches_signature(args): - # First check if the signature even matches, if not we don't - # need to infer anything. - continue - bound_method = BoundMethod(self, signature.value) - all_annotations = py__annotations__(signature.value.tree_node) - type_var_dict = infer_type_vars_for_execution(bound_method, args, all_annotations) - if type_var_dict: - defined, = self.class_value.define_generics( - infer_type_vars_for_execution(signature.value, args, all_annotations), - ) - debug.dbg('Inferred instance value as %s', defined, color='BLUE') - return defined - return None - - def get_annotated_class_object(self): - return self._get_annotated_class_object() or self.class_value - def get_filters(self, origin_scope=None, include_self_names=True): class_value = self.get_annotated_class_object() if include_self_names: @@ -254,6 +211,38 @@ class TreeInstance(AbstractInstanceValue): for signature in init.get_signatures(): yield signature.value + @inference_state_method_cache() + def create_instance_context(self, class_context, node): + if node.parent.type in ('funcdef', 'classdef'): + node = node.parent + scope = get_parent_scope(node) + if scope == class_context.tree_node: + return class_context + else: + parent_context = self.create_instance_context(class_context, scope) + if scope.type == 'funcdef': + func = FunctionValue.from_context( + parent_context, + scope, + ) + bound_method = BoundMethod(self, func) + if scope.name.value == '__init__' and parent_context == class_context: + if hasattr(self, 'arguments'): + return bound_method.as_context(self.arguments) + else: + return bound_method.as_context() + else: + return bound_method.as_context() + elif scope.type == 'classdef': + class_context = ClassValue(self.inference_state, parent_context, scope) + return class_context.as_context() + elif scope.type in ('comp_for', 'sync_comp_for'): + # Comprehensions currently don't have a special scope in Jedi. + return self.create_instance_context(class_context, scope) + else: + raise NotImplementedError + return class_context + def py__getattribute__alternatives(self, string_name): ''' Since nothing was inferred, now check the __getattr__ and @@ -276,29 +265,6 @@ class TreeInstance(AbstractInstanceValue): self.get_function_slot_names(u'__getattribute__')) return self.execute_function_slots(names, name) - def py__simple_getitem__(self, index): - if self.array_type == 'dict': - # Logic for dict({'foo': bar}) and dict(foo=bar) - # reversed, because: - # >>> dict({'a': 1}, a=3) - # {'a': 3} - # TODO tuple initializations - # >>> dict([('a', 4)]) - # {'a': 4} - for key, lazy_context in reversed(list(self.arguments.unpack())): - if key is None: - values = ValueSet.from_sets( - dct_value.py__simple_getitem__(index) - for dct_value in lazy_context.infer() - if dct_value.array_type == 'dict' - ) - if values: - return values - else: - if key == index: - return lazy_context.infer() - return super(TreeInstance, self).py__simple_getitem__(index) - def py__getitem__(self, index_value_set, contextualized_node): names = self.get_function_slot_names(u'__getitem__') if not names: @@ -373,55 +339,84 @@ class TreeInstance(AbstractInstanceValue): for name in names ) - @inference_state_method_cache() - def create_instance_context(self, class_context, node): - if node.parent.type in ('funcdef', 'classdef'): - node = node.parent - scope = get_parent_scope(node) - if scope == class_context.tree_node: - return class_context - else: - parent_context = self.create_instance_context(class_context, scope) - if scope.type == 'funcdef': - func = FunctionValue.from_context( - parent_context, - scope, + +class TreeInstance(_BaseTreeInstance): + def __init__(self, inference_state, parent_context, class_value, arguments): + # I don't think that dynamic append lookups should happen here. That + # sounds more like something that should go to py__iter__. + if class_value.py__name__() in ['list', 'set'] \ + and parent_context.get_root_context().is_builtins_module(): + # compare the module path with the builtin name. + if settings.dynamic_array_additions: + arguments = get_dynamic_array_instance(self, arguments) + + super(_BaseTreeInstance, self).__init__(inference_state, parent_context, + class_value) + self.arguments = arguments + self.tree_node = class_value.tree_node + + # This can recurse, if the initialization of the class includes a reference + # to itself. + @inference_state_method_cache(default=None) + def _get_annotated_class_object(self): + from jedi.inference.gradual.annotation import py__annotations__, \ + infer_type_vars_for_execution + + args = InstanceArguments(self, self.arguments) + for signature in self.class_value.py__getattribute__('__init__').get_signatures(): + # Just take the first result, it should always be one, because we + # control the typeshed code. + if not signature.matches_signature(args): + # First check if the signature even matches, if not we don't + # need to infer anything. + continue + bound_method = BoundMethod(self, signature.value) + all_annotations = py__annotations__(signature.value.tree_node) + type_var_dict = infer_type_vars_for_execution(bound_method, args, all_annotations) + if type_var_dict: + defined, = self.class_value.define_generics( + infer_type_vars_for_execution(signature.value, args, all_annotations), ) - bound_method = BoundMethod(self, func) - if scope.name.value == '__init__' and parent_context == class_context: - return bound_method.as_context(self.arguments) + debug.dbg('Inferred instance value as %s', defined, color='BLUE') + return defined + return None + + def get_annotated_class_object(self): + return self._get_annotated_class_object() or self.class_value + + def py__simple_getitem__(self, index): + if self.array_type == 'dict': + # Logic for dict({'foo': bar}) and dict(foo=bar) + # reversed, because: + # >>> dict({'a': 1}, a=3) + # {'a': 3} + # TODO tuple initializations + # >>> dict([('a', 4)]) + # {'a': 4} + for key, lazy_context in reversed(list(self.arguments.unpack())): + if key is None: + values = ValueSet.from_sets( + dct_value.py__simple_getitem__(index) + for dct_value in lazy_context.infer() + if dct_value.array_type == 'dict' + ) + if values: + return values else: - return bound_method.as_context() - elif scope.type == 'classdef': - class_context = ClassValue(self.inference_state, parent_context, scope) - return class_context.as_context() - elif scope.type in ('comp_for', 'sync_comp_for'): - # Comprehensions currently don't have a special scope in Jedi. - return self.create_instance_context(class_context, scope) - else: - raise NotImplementedError - return class_context + if key == index: + return lazy_context.infer() + return super(TreeInstance, self).py__simple_getitem__(index) def __repr__(self): return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_value, self.arguments) -class AnonymousInstance(TreeInstance): - def __init__(self, inference_state, parent_context, class_value): - super(AnonymousInstance, self).__init__( - inference_state, - parent_context, - class_value, - arguments=AnonymousInstanceArguments(self), - ) - - def get_annotated_class_object(self): - return self.class_value # This is the default. +class AnonymousInstance(_BaseTreeInstance): + pass class CompiledInstanceName(compiled.CompiledName): - def __init__(self, inference_state, instance, klass, name): super(CompiledInstanceName, self).__init__( inference_state, @@ -472,9 +467,7 @@ class BoundMethod(FunctionMixin, ValueWrapper): return c def _get_arguments(self, arguments): - if arguments is None: - arguments = AnonymousInstanceArguments(self.instance) - + assert arguments is not None return InstanceArguments(self.instance, arguments) def _as_context(self, arguments=None):