From c7fc715535ab6192f096583d3dc07e1e7ff0ff94 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Thu, 18 Jul 2019 11:20:54 +0200 Subject: [PATCH] Use class filters in instances differently so metaclass plugins work, fixes #1090 --- jedi/evaluate/compiled/context.py | 12 ++++----- jedi/evaluate/context/instance.py | 42 ++++++++++++++++--------------- jedi/evaluate/context/klass.py | 7 +++--- jedi/evaluate/gradual/typing.py | 3 +++ jedi/plugins/stdlib.py | 3 +-- 5 files changed, 36 insertions(+), 31 deletions(-) diff --git a/jedi/evaluate/compiled/context.py b/jedi/evaluate/compiled/context.py index b5aed86c..a101d9ff 100644 --- a/jedi/evaluate/compiled/context.py +++ b/jedi/evaluate/compiled/context.py @@ -380,14 +380,14 @@ class CompiledObjectFilter(AbstractFilter): def __init__(self, evaluator, compiled_object, is_instance=False): self._evaluator = evaluator - self._compiled_object = compiled_object + self.compiled_object = compiled_object self.is_instance = is_instance def get(self, name): return self._get( name, - lambda: self._compiled_object.access_handle.is_allowed_getattr(name), - lambda: self._compiled_object.access_handle.dir(), + lambda: self.compiled_object.access_handle.is_allowed_getattr(name), + lambda: self.compiled_object.access_handle.dir(), check_has_attribute=True ) @@ -419,7 +419,7 @@ class CompiledObjectFilter(AbstractFilter): def values(self): from jedi.evaluate.compiled import builtin_from_name names = [] - needs_type_completions, dir_infos = self._compiled_object.access_handle.get_dir_infos() + needs_type_completions, dir_infos = self.compiled_object.access_handle.get_dir_infos() for name in dir_infos: names += self._get( name, @@ -434,10 +434,10 @@ class CompiledObjectFilter(AbstractFilter): return names def _create_name(self, name): - return self.name_class(self._evaluator, self._compiled_object, name) + return self.name_class(self._evaluator, self.compiled_object, name) def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self._compiled_object) + return "<%s: %s>" % (self.__class__.__name__, self.compiled_object) docstr_defaults = { diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index ba0b5cf5..5a55bd9c 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -3,6 +3,7 @@ from abc import abstractproperty from jedi import debug from jedi import settings from jedi.evaluate import compiled +from jedi.evaluate.compiled.context import CompiledObjectFilter from jedi.evaluate.helpers import contexts_from_qualified_names from jedi.evaluate.filters import AbstractFilter from jedi.evaluate.names import ContextName, TreeNameDefinition @@ -136,11 +137,19 @@ class AbstractInstanceContext(Context): # compiled objects to search for self variables. yield SelfAttributeFilter(self.evaluator, self, cls, origin_scope) - for cls in class_context.py__mro__(): - if isinstance(cls, compiled.CompiledObject): - yield CompiledInstanceClassFilter(self.evaluator, self, cls) + class_filters = class_context.get_filters( + search_global=False, + origin_scope=origin_scope, + is_instance=True, + ) + for f in class_filters: + if isinstance(f, ClassFilter): + yield InstanceClassFilter(self.evaluator, self, f) + elif isinstance(f, CompiledObjectFilter): + yield CompiledInstanceClassFilter(self.evaluator, self, f) else: - yield InstanceClassFilter(self.evaluator, self, cls, origin_scope) + # Propably from the metaclass. + yield f def py__getitem__(self, index_context_set, contextualized_node): names = self.get_function_slot_names(u'__getitem__') @@ -328,7 +337,6 @@ class CompiledInstanceName(compiled.CompiledName): name.string_name ) self._instance = instance - self._class = klass self._class_member_name = name @iterator_to_context_set @@ -343,11 +351,10 @@ class CompiledInstanceName(compiled.CompiledName): class CompiledInstanceClassFilter(AbstractFilter): name_class = CompiledInstanceName - def __init__(self, evaluator, instance, klass): + def __init__(self, evaluator, instance, f): self._evaluator = evaluator self._instance = instance - self._class = klass - self._class_filter = next(klass.get_filters(is_instance=True)) + self._class_filter = f def get(self, name): return self._convert(self._class_filter.get(name)) @@ -356,8 +363,9 @@ class CompiledInstanceClassFilter(AbstractFilter): return self._convert(self._class_filter.values()) def _convert(self, names): + klass = self._class_filter.compiled_object return [ - CompiledInstanceName(self._evaluator, self._instance, self._class, n) + CompiledInstanceName(self._evaluator, self._instance, klass, n) for n in names ] @@ -454,15 +462,9 @@ class InstanceClassFilter(AbstractFilter): resulting names in LazyINstanceClassName. The idea is that the class name filtering can be very flexible and always be reflected in instances. """ - def __init__(self, evaluator, context, class_context, origin_scope): - 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, - )) - assert isinstance(self._class_filter, ClassFilter), self._class_filter + def __init__(self, evaluator, instance, class_filter): + self._instance = instance + self._class_filter = class_filter def get(self, name): return self._convert(self._class_filter.get(name, from_instance=True)) @@ -471,10 +473,10 @@ class InstanceClassFilter(AbstractFilter): return self._convert(self._class_filter.values(from_instance=True)) def _convert(self, names): - return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names] + return [LazyInstanceClassName(self._instance, self._class_filter.context, n) for n in names] def __repr__(self): - return '<%s for %s>' % (self.__class__.__name__, self._class_context) + return '<%s for %s>' % (self.__class__.__name__, self._class_filter.context) class SelfAttributeFilter(ClassFilter): diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index aac65e6a..5d424f97 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -331,7 +331,8 @@ class ClassContext(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBa for lazy_base in self.py__bases__(): for context in lazy_base.infer(): - contexts = context.get_metaclasses() - if contexts: - return contexts + if context.is_class(): + contexts = context.get_metaclasses() + if contexts: + return contexts return NO_CONTEXTS diff --git a/jedi/evaluate/gradual/typing.py b/jedi/evaluate/gradual/typing.py index b9df8732..f3e42f85 100644 --- a/jedi/evaluate/gradual/typing.py +++ b/jedi/evaluate/gradual/typing.py @@ -209,6 +209,9 @@ class _TypingClassMixin(object): self.evaluator.builtins_module.py__getattribute__('object') )] + def get_metaclasses(self): + return [] + class TypingClassContextWithIndex(_TypingClassMixin, TypingContextWithIndex, ClassMixin): pass diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 088fb698..0a6e36a6 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -612,7 +612,6 @@ def get_metaclass_filters(func): for metaclass in metaclasses: if metaclass.py__name__() == 'EnumMeta' \ and metaclass.get_root_context().py__name__() == 'enum': - print('cont', cls) filter_ = ParserTreeFilter(cls.evaluator, context=cls) return [DictFilter({ name.string_name: EnumInstance(cls, name).name for name in filter_.values() @@ -638,7 +637,7 @@ class EnumInstance(LazyContextWrapper): def get_filters(self, search_global=False, position=None, origin_scope=None): yield DictFilter(dict( - name=self._name.string_name, + name=compiled.create_simple_object(self.evaluator, self._name.string_name).name, value=self._name, )) for f in self._get_wrapped_context().get_filters():