diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index b2af36ab..a6ff4dd5 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -169,7 +169,7 @@ class AbstractInstanceContext(Context): def create_init_executions(self): for name in self.get_function_slot_names(u'__init__'): - if isinstance(name, SelfName): + if isinstance(name, LazyInstanceClassName): function = FunctionContext.from_context( self.parent_context, name.tree_name.parent @@ -351,10 +351,15 @@ class SelfName(filters.TreeNameDefinition): return self._instance.create_instance_context(self.class_context, self.tree_name) -class LazyInstanceClassName(SelfName): +class LazyInstanceClassName(object): + def __init__(self, instance, class_context, class_member_name): + self._instance = instance + self.class_context = class_context + self._class_member_name = class_member_name + @iterator_to_context_set def infer(self): - for result_context in super(LazyInstanceClassName, self).infer(): + for result_context in self._class_member_name.infer(): if isinstance(result_context, FunctionContext): # Classes are never used to resolve anything within the # functions. Only other functions and modules will resolve @@ -364,12 +369,45 @@ class LazyInstanceClassName(SelfName): for c in apply_py__get__(result_context, self._instance): yield c + def __getattr__(self, name): + return getattr(self._class_member_name, name) -class InstanceClassFilter(filters.ParserTreeFilter): + +class InstanceClassFilter(filters.AbstractFilter): + """ + This filter is special in that it uses the class filter and wraps the + resulting names in LazyINstanceClassName. The idea is that the class name + filtering can be very flexible and always be reflected in instances. + """ name_class = LazyInstanceClassName def __init__(self, evaluator, context, class_context, origin_scope): - super(InstanceClassFilter, self).__init__( + self._instance = context + self._class_context = class_context + self._class_filter = next(class_context.get_filters( + search_global=False, + origin_scope=origin_scope, + is_instance=True, + )) + + def get(self, name): + return self._convert(self._class_filter.get(name)) + + def values(self): + return self._convert(self._class_filter.values()) + + def _convert(self, names): + return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names] + + +class SelfAttributeFilter(filters.ParserTreeFilter): + """ + This class basically filters all the use cases where `self.*` was assigned. + """ + name_class = SelfName + + def __init__(self, evaluator, context, class_context, origin_scope): + super(SelfAttributeFilter, self).__init__( evaluator=evaluator, context=context, node_context=class_context, @@ -377,32 +415,6 @@ class InstanceClassFilter(filters.ParserTreeFilter): ) self._class_context = class_context - def _equals_origin_scope(self): - node = self._origin_scope - while node is not None: - if node == self._parser_scope or node == self.context: - return True - node = get_parent_scope(node) - return False - - def _access_possible(self, name): - return not name.value.startswith('__') or name.value.endswith('__') \ - or self._equals_origin_scope() - - def _filter(self, names): - names = super(InstanceClassFilter, self)._filter(names) - return [name for name in names if self._access_possible(name)] - - def _convert_names(self, names): - return [self.name_class(self.context, self._class_context, name) for name in names] - - -class SelfAttributeFilter(InstanceClassFilter): - """ - This class basically filters all the use cases where `self.*` was assigned. - """ - name_class = SelfName - def _filter(self, names): names = self._filter_self_names(names) if isinstance(self._parser_scope, compiled.CompiledObject) and False: @@ -421,6 +433,21 @@ class SelfAttributeFilter(InstanceClassFilter): if name.is_definition() and self._access_possible(name): yield name + def _equals_origin_scope(self): + node = self._origin_scope + while node is not None: + if node == self._parser_scope or node == self.context: + return True + node = get_parent_scope(node) + return False + + def _access_possible(self, name): + return not name.value.startswith('__') or name.value.endswith('__') \ + or self._equals_origin_scope() + + def _convert_names(self, names): + return [self.name_class(self.context, self._class_context, name) for name in names] + def _check_flows(self, names): return names diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index e93306c1..182c1d41 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -59,9 +59,10 @@ def apply_py__get__(context, base_context): class ClassName(TreeNameDefinition): - def __init__(self, parent_context, tree_name, name_context): + def __init__(self, parent_context, tree_name, name_context, apply_decorators): super(ClassName, self).__init__(parent_context, tree_name) self._name_context = name_context + self._apply_decorators = apply_decorators @iterator_to_context_set def infer(self): @@ -73,16 +74,29 @@ class ClassName(TreeNameDefinition): self.parent_context.evaluator, self._name_context, self.tree_name) for result_context in inferred: - for c in apply_py__get__(result_context, self.parent_context): - yield c + if self._apply_decorators: + for c in apply_py__get__(result_context, self.parent_context): + yield c + else: + yield result_context class ClassFilter(ParserTreeFilter): name_class = ClassName + def __init__(self, *args, **kwargs): + self._is_instance = kwargs.pop('is_instance') + super(ClassFilter, self).__init__(*args, **kwargs) + def _convert_names(self, names): - return [self.name_class(self.context, name, self._node_context) - for name in names] + return [ + self.name_class( + parent_context=self.context, + tree_name=name, + name_context=self._node_context, + apply_decorators=not self._is_instance, + ) for name in names + ] def _equals_origin_scope(self): node = self._origin_scope @@ -182,7 +196,9 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): else: yield ClassFilter( self.evaluator, self, node_context=cls, - origin_scope=origin_scope) + origin_scope=origin_scope, + is_instance=is_instance + ) def is_class(self): return True