Use class filters in instances differently so metaclass plugins work, fixes #1090

This commit is contained in:
Dave Halter
2019-07-18 11:20:54 +02:00
parent eeea88046e
commit c7fc715535
5 changed files with 36 additions and 31 deletions

View File

@@ -380,14 +380,14 @@ class CompiledObjectFilter(AbstractFilter):
def __init__(self, evaluator, compiled_object, is_instance=False): def __init__(self, evaluator, compiled_object, is_instance=False):
self._evaluator = evaluator self._evaluator = evaluator
self._compiled_object = compiled_object self.compiled_object = compiled_object
self.is_instance = is_instance self.is_instance = is_instance
def get(self, name): def get(self, name):
return self._get( return self._get(
name, name,
lambda: self._compiled_object.access_handle.is_allowed_getattr(name), lambda: self.compiled_object.access_handle.is_allowed_getattr(name),
lambda: self._compiled_object.access_handle.dir(), lambda: self.compiled_object.access_handle.dir(),
check_has_attribute=True check_has_attribute=True
) )
@@ -419,7 +419,7 @@ class CompiledObjectFilter(AbstractFilter):
def values(self): def values(self):
from jedi.evaluate.compiled import builtin_from_name from jedi.evaluate.compiled import builtin_from_name
names = [] 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: for name in dir_infos:
names += self._get( names += self._get(
name, name,
@@ -434,10 +434,10 @@ class CompiledObjectFilter(AbstractFilter):
return names return names
def _create_name(self, name): 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): def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self._compiled_object) return "<%s: %s>" % (self.__class__.__name__, self.compiled_object)
docstr_defaults = { docstr_defaults = {

View File

@@ -3,6 +3,7 @@ from abc import abstractproperty
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate.compiled.context import CompiledObjectFilter
from jedi.evaluate.helpers import contexts_from_qualified_names from jedi.evaluate.helpers import contexts_from_qualified_names
from jedi.evaluate.filters import AbstractFilter from jedi.evaluate.filters import AbstractFilter
from jedi.evaluate.names import ContextName, TreeNameDefinition from jedi.evaluate.names import ContextName, TreeNameDefinition
@@ -136,11 +137,19 @@ 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 class_context.py__mro__(): class_filters = class_context.get_filters(
if isinstance(cls, compiled.CompiledObject): search_global=False,
yield CompiledInstanceClassFilter(self.evaluator, self, cls) 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: else:
yield InstanceClassFilter(self.evaluator, self, cls, origin_scope) # Propably from the metaclass.
yield f
def py__getitem__(self, index_context_set, contextualized_node): def py__getitem__(self, index_context_set, contextualized_node):
names = self.get_function_slot_names(u'__getitem__') names = self.get_function_slot_names(u'__getitem__')
@@ -328,7 +337,6 @@ class CompiledInstanceName(compiled.CompiledName):
name.string_name name.string_name
) )
self._instance = instance self._instance = instance
self._class = klass
self._class_member_name = name self._class_member_name = name
@iterator_to_context_set @iterator_to_context_set
@@ -343,11 +351,10 @@ class CompiledInstanceName(compiled.CompiledName):
class CompiledInstanceClassFilter(AbstractFilter): class CompiledInstanceClassFilter(AbstractFilter):
name_class = CompiledInstanceName name_class = CompiledInstanceName
def __init__(self, evaluator, instance, klass): def __init__(self, evaluator, instance, f):
self._evaluator = evaluator self._evaluator = evaluator
self._instance = instance self._instance = instance
self._class = klass self._class_filter = f
self._class_filter = next(klass.get_filters(is_instance=True))
def get(self, name): def get(self, name):
return self._convert(self._class_filter.get(name)) return self._convert(self._class_filter.get(name))
@@ -356,8 +363,9 @@ class CompiledInstanceClassFilter(AbstractFilter):
return self._convert(self._class_filter.values()) return self._convert(self._class_filter.values())
def _convert(self, names): def _convert(self, names):
klass = self._class_filter.compiled_object
return [ return [
CompiledInstanceName(self._evaluator, self._instance, self._class, n) CompiledInstanceName(self._evaluator, self._instance, klass, n)
for n in names for n in names
] ]
@@ -454,15 +462,9 @@ class InstanceClassFilter(AbstractFilter):
resulting names in LazyINstanceClassName. The idea is that the class name resulting names in LazyINstanceClassName. The idea is that the class name
filtering can be very flexible and always be reflected in instances. filtering can be very flexible and always be reflected in instances.
""" """
def __init__(self, evaluator, context, class_context, origin_scope): def __init__(self, evaluator, instance, class_filter):
self._instance = context self._instance = instance
self._class_context = class_context self._class_filter = class_filter
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 get(self, name): def get(self, name):
return self._convert(self._class_filter.get(name, from_instance=True)) 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)) return self._convert(self._class_filter.values(from_instance=True))
def _convert(self, names): 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): 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): class SelfAttributeFilter(ClassFilter):

View File

@@ -331,7 +331,8 @@ class ClassContext(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBa
for lazy_base in self.py__bases__(): for lazy_base in self.py__bases__():
for context in lazy_base.infer(): for context in lazy_base.infer():
contexts = context.get_metaclasses() if context.is_class():
if contexts: contexts = context.get_metaclasses()
return contexts if contexts:
return contexts
return NO_CONTEXTS return NO_CONTEXTS

View File

@@ -209,6 +209,9 @@ class _TypingClassMixin(object):
self.evaluator.builtins_module.py__getattribute__('object') self.evaluator.builtins_module.py__getattribute__('object')
)] )]
def get_metaclasses(self):
return []
class TypingClassContextWithIndex(_TypingClassMixin, TypingContextWithIndex, ClassMixin): class TypingClassContextWithIndex(_TypingClassMixin, TypingContextWithIndex, ClassMixin):
pass pass

View File

@@ -612,7 +612,6 @@ def get_metaclass_filters(func):
for metaclass in metaclasses: for metaclass in metaclasses:
if metaclass.py__name__() == 'EnumMeta' \ if metaclass.py__name__() == 'EnumMeta' \
and metaclass.get_root_context().py__name__() == 'enum': and metaclass.get_root_context().py__name__() == 'enum':
print('cont', cls)
filter_ = ParserTreeFilter(cls.evaluator, context=cls) filter_ = ParserTreeFilter(cls.evaluator, context=cls)
return [DictFilter({ return [DictFilter({
name.string_name: EnumInstance(cls, name).name for name in filter_.values() 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): def get_filters(self, search_global=False, position=None, origin_scope=None):
yield DictFilter(dict( yield DictFilter(dict(
name=self._name.string_name, name=compiled.create_simple_object(self.evaluator, self._name.string_name).name,
value=self._name, value=self._name,
)) ))
for f in self._get_wrapped_context().get_filters(): for f in self._get_wrapped_context().get_filters():