diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index ccd65cae..f85126d3 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -74,7 +74,6 @@ from jedi.evaluate.utils import unite from jedi.evaluate import imports from jedi.evaluate import recursion from jedi.evaluate.cache import evaluator_function_cache -from jedi.evaluate import compiled from jedi.evaluate import helpers from jedi.evaluate.filters import TreeNameDefinition, ParamName from jedi.evaluate.base_context import ContextualizedName, ContextualizedNode, \ diff --git a/jedi/evaluate/base_context.py b/jedi/evaluate/base_context.py index 09e9eb2e..4b28f5e0 100644 --- a/jedi/evaluate/base_context.py +++ b/jedi/evaluate/base_context.py @@ -29,6 +29,9 @@ class HelperContextMixin: def execute_evaluated(self, *value_list): return execute_evaluated(self, *value_list) + def execute_annotation(self): + return self.execute_evaluated() + def merge_types_of_iterate(self, contextualized_node=None, is_async=False): return ContextSet.from_sets( lazy_context.infer() @@ -52,6 +55,9 @@ class HelperContextMixin: return f.filter_name(filters) return f.find(filters, attribute_lookup=not search_global) + def eval_node(self, node): + return self.evaluator.eval_element(self, node) + def is_sub_class_of(self, class_context): from jedi.evaluate.context.klass import py__mro__ for cls in py__mro__(self): @@ -60,8 +66,6 @@ class HelperContextMixin: return False def is_same_class(self, class2): - if isinstance(class2, ContextWrapper): - class2 = class2._wrapped_context # Class matching should prefer comparisons that are not this function. if type(class2).is_same_class != HelperContextMixin.is_same_class: return class2.is_same_class(self) @@ -113,9 +117,6 @@ class Context(HelperContextMixin, BaseContext): else: return iter_method() - def eval_node(self, node): - return self.evaluator.eval_element(self, node) - def py__getitem__(self, index_context_set, contextualized_node): from jedi.evaluate import analysis # TODO this context is probably not right. @@ -127,9 +128,6 @@ class Context(HelperContextMixin, BaseContext): ) return NO_CONTEXTS - def execute_annotation(self): - return execute_evaluated(self) - def create_context(self, node, node_is_context=False, node_is_object=False): return self.evaluator.create_context(self, node, node_is_context, node_is_object) @@ -193,6 +191,11 @@ class ContextWrapper(HelperContextMixin, object): from jedi.evaluate.compiled import CompiledContextName return CompiledContextName(self, wrapped_name.string_name) + @classmethod + @evaluator_as_method_param_cache() + def create_cached(cls, evaluator, *args, **kwargs): + return cls(*args, **kwargs) + def __getattr__(self, name): return getattr(self._wrapped_context, name) diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index 3ca8ed21..48eab33f 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -316,28 +316,20 @@ class FunctionExecutionContext(TreeContext): .py__getattribute__('AsyncGenerator') yield_contexts = self.merge_yield_contexts(is_async=True) + # The contravariant doesn't seem to be defined. + generics = (yield_contexts.py__class__(), NO_CONTEXTS) return ContextSet( - AnnotatedSubClass( - evaluator, - parent_context=c.parent_context, - tree_node=c.tree_node, - # The contravariant doesn't seem to be defined. - given_types=(yield_contexts.py__class__(), NO_CONTEXTS) - ) for c in async_generator_classes + AnnotatedSubClass(c, generics) for c in async_generator_classes ).execute_annotation() else: if evaluator.environment.version_info < (3, 5): return NO_CONTEXTS async_classes = evaluator.typing_module.py__getattribute__('Coroutine') return_contexts = self.get_return_values() + # Only the first generic is relevant. + generics = (return_contexts.py__class__(), NO_CONTEXTS, NO_CONTEXTS) return ContextSet( - AnnotatedSubClass( - evaluator, - parent_context=c.parent_context, - tree_node=c.tree_node, - # Only the first generic is relevant. - given_types=(return_contexts.py__class__(), NO_CONTEXTS, NO_CONTEXTS) - ) for c in async_classes + AnnotatedSubClass(c, generics) for c in async_classes ).execute_annotation() else: if is_generator: diff --git a/jedi/evaluate/context/iterable.py b/jedi/evaluate/context/iterable.py index aa176bad..6b53ae6c 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -210,10 +210,8 @@ class Sequence(BuiltinOverwrite, IterableMixin): def get_object(self): from jedi.evaluate.context.typing import AnnotatedSubClass klass = compiled.builtin_from_name(self.evaluator, self.array_type) - return AnnotatedSubClass( - self.evaluator, klass.parent_context, klass.tree_node, - self._get_generics() - ).execute_annotation() + # TODO is this execute annotation wrong? it returns a context set?! + return AnnotatedSubClass(klass, self._get_generics()).execute_annotation() def py__bool__(self): return None # We don't know the length, because of appends. diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index 1e604c39..e0fd929d 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -42,7 +42,7 @@ from jedi._compatibility import use_metaclass from jedi.parser_utils import get_parent_scope from jedi.evaluate.cache import evaluator_method_cache, CachedMetaClass from jedi.evaluate import compiled -from jedi.evaluate.lazy_context import LazyKnownContext +from jedi.evaluate.lazy_context import LazyKnownContexts from jedi.evaluate.filters import ParserTreeFilter, TreeNameDefinition, \ ContextName from jedi.evaluate.arguments import unpack_arglist @@ -68,8 +68,9 @@ def py__mro__(context): except AttributeError: pass else: - # Currently only used for compiled objects. - return method() + if not isinstance(context, ClassMixin): + # Currently only used for compiled objects. + return method() def add(cls): if cls not in mro: @@ -161,7 +162,77 @@ class ClassFilter(ParserTreeFilter): return [name for name in names if self._access_possible(name)] -class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): +class ClassMixin(object): + def is_class(self): + return True + + def py__call__(self, arguments): + from jedi.evaluate.context import TreeInstance + return ContextSet([TreeInstance(self.evaluator, self.parent_context, self, arguments)]) + + def py__class__(self): + return compiled.builtin_from_name(self.evaluator, u'type') + + @property + def name(self): + return ContextName(self, self.tree_node.name) + + def py__name__(self): + return self.name.string_name + + def get_function_slot_names(self, name): + for filter in self.get_filters(search_global=False): + names = filter.get(name) + if names: + return names + return [] + + def get_param_names(self): + for name in self.get_function_slot_names(u'__init__'): + for context_ in name.infer(): + try: + method = context_.get_param_names + except AttributeError: + pass + else: + return list(method())[1:] + return [] + + def py__mro__(self): + return py__mro__(self) + + def get_filters(self, search_global=False, until_position=None, + origin_scope=None, is_instance=False): + if search_global: + yield ParserTreeFilter( + self.evaluator, + context=self, + until_position=until_position, + origin_scope=origin_scope + ) + else: + for cls in py__mro__(self): + if isinstance(cls, compiled.CompiledObject): + for filter in cls.get_filters(is_instance=is_instance): + yield filter + else: + yield ClassFilter( + self.evaluator, self, node_context=cls, + origin_scope=origin_scope, + is_instance=is_instance + ) + if not is_instance and self: + # Return completions of the meta class. + from jedi.evaluate.compiled import builtin_from_name + type_ = builtin_from_name(self.evaluator, u'type') + yield ClassFilter( + self.evaluator, self, node_context=type_, + origin_scope=origin_scope, + is_instance=is_instance + ) + + +class ClassContext(use_metaclass(CachedMetaClass, ClassMixin, TreeContext)): """ This class is not only important to extend `tree.Class`, it is also a important for descriptors (if the descriptor methods are evaluated or not). @@ -194,72 +265,9 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): args = arguments.TreeArguments(self.evaluator, self.parent_context, arglist) return [value for key, value in args.unpack() if key is None] else: - return [LazyKnownContext(compiled.builtin_from_name(self.evaluator, u'object'))] - - def py__call__(self, arguments): - from jedi.evaluate.context import TreeInstance - return ContextSet([TreeInstance(self.evaluator, self.parent_context, self, arguments)]) - - def py__class__(self): - return compiled.builtin_from_name(self.evaluator, u'type') - - def get_filters(self, search_global=False, until_position=None, - origin_scope=None, is_instance=False): - if search_global: - yield ParserTreeFilter( - self.evaluator, - context=self, - until_position=until_position, - origin_scope=origin_scope - ) - else: - for cls in py__mro__(self): - if isinstance(cls, compiled.CompiledObject): - for filter in cls.get_filters(is_instance=is_instance): - yield filter - else: - yield ClassFilter( - self.evaluator, self, node_context=cls, - origin_scope=origin_scope, - is_instance=is_instance - ) - if not is_instance and self: - # Return completions of the meta class. - from jedi.evaluate.compiled import builtin_from_name - type_ = builtin_from_name(self.evaluator, u'type') - yield ClassFilter( - self.evaluator, self, node_context=type_, - origin_scope=origin_scope, - is_instance=is_instance - ) - - def is_class(self): - return True - - def get_function_slot_names(self, name): - for filter in self.get_filters(search_global=False): - names = filter.get(name) - if names: - return names - return [] - - def get_param_names(self): - for name in self.get_function_slot_names(u'__init__'): - for context_ in name.infer(): - try: - method = context_.get_param_names - except AttributeError: - pass - else: - return list(method())[1:] - return [] - - @property - def name(self): - return ContextName(self, self.tree_node.name) - - def py__name__(self): - return self.name.string_name + return [LazyKnownContexts( + self.evaluator.builtins_module.py__getattribute__('object') + )] def py__getitem__(self, index_context_set, contextualized_node): from jedi.evaluate.context.typing import AnnotatedClass @@ -267,9 +275,7 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): return ContextSet([self]) return ContextSet( AnnotatedClass( - self.evaluator, - self.parent_context, - self.tree_node, + self, index_context, context_of_index=contextualized_node.context, ) @@ -285,9 +291,7 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext)): if type_var_dict: return AnnotatedSubClass( - self.evaluator, - self.parent_context, - self.tree_node, + self, given_types=tuple(remap_type_vars()) ) return self diff --git a/jedi/evaluate/context/typing.py b/jedi/evaluate/context/typing.py index b684170e..4b1239d0 100644 --- a/jedi/evaluate/context/typing.py +++ b/jedi/evaluate/context/typing.py @@ -9,7 +9,7 @@ from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.compiled import builtin_from_name from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context, \ iterator_to_context_set, HelperContextMixin, ContextWrapper -from jedi.evaluate.lazy_context import LazyKnownContexts, LazyKnownContext +from jedi.evaluate.lazy_context import LazyKnownContexts from jedi.evaluate.context.iterable import SequenceLiteralContext from jedi.evaluate.arguments import repack_with_argument_clinic from jedi.evaluate.utils import to_list @@ -17,7 +17,7 @@ from jedi.evaluate.filters import FilterWrapper, NameWrapper, \ AbstractTreeName, AbstractNameDefinition, ContextName from jedi.evaluate.helpers import is_string from jedi.evaluate.imports import Importer -from jedi.evaluate.context.klass import py__mro__, ClassContext +from jedi.evaluate.context.klass import py__mro__, ClassMixin _PROXY_CLASS_TYPES = 'Tuple Generic Protocol Callable Type'.split() _TYPE_ALIAS_TYPES = { @@ -192,8 +192,10 @@ class TypingContext(_BaseTypingContext): class TypingClassMixin(object): - def py__bases__(self,): - return [LazyKnownContext(builtin_from_name(self.evaluator, u'object'))] + def py__bases__(self): + return [LazyKnownContexts( + self.evaluator.builtins_module.py__getattribute__('object') + )] class TypingClassContextWithIndex(TypingClassMixin, TypingContextWithIndex): @@ -490,7 +492,7 @@ class TypeVarFilter(object): return [] -class AbstractAnnotatedClass(ClassContext): +class AbstractAnnotatedClass(ClassMixin, ContextWrapper): def get_type_var_filter(self): return TypeVarFilter(self.get_given_types(), self.list_type_vars()) @@ -564,29 +566,26 @@ class AbstractAnnotatedClass(ClassContext): return self return AnnotatedSubClass( - self.evaluator, - self.parent_context, - self.tree_node, + self._wrapped_context, given_types=tuple(new_generics) ) def __repr__(self): - return '<%s: %s@%s%s>' % ( + return '<%s: %s%s>' % ( self.__class__.__name__, - self.name.string_name, - self.name.tree_name.start_pos, + self._wrapped_context, list(self.get_given_types()), ) @to_list def py__bases__(self): - for base in super(AbstractAnnotatedClass, self).py__bases__(): + for base in self._wrapped_context.py__bases__(): yield LazyAnnotatedBaseClass(self, base) class AnnotatedClass(AbstractAnnotatedClass): - def __init__(self, evaluator, parent_context, tree_node, index_context, context_of_index): - super(AnnotatedClass, self).__init__(evaluator, parent_context, tree_node) + def __init__(self, class_context, index_context, context_of_index): + super(AnnotatedClass, self).__init__(class_context) self._index_context = index_context self._context_of_index = context_of_index @@ -596,8 +595,8 @@ class AnnotatedClass(AbstractAnnotatedClass): class AnnotatedSubClass(AbstractAnnotatedClass): - def __init__(self, evaluator, parent_context, tree_node, given_types): - super(AnnotatedSubClass, self).__init__(evaluator, parent_context, tree_node) + def __init__(self, class_context, given_types): + super(AnnotatedSubClass, self).__init__(class_context) self._given_types = given_types def get_given_types(self): @@ -616,8 +615,7 @@ class LazyAnnotatedBaseClass(object): # Here we have to recalculate the given types. yield AnnotatedSubClass.create_cached( base.evaluator, - base.parent_context, - base.tree_node, + base._wrapped_context, tuple(self._remap_type_vars(base)), ) else: diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index 8dd071b8..abb4cf0f 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -64,7 +64,7 @@ def _py__stop_iteration_returns(generators): @debug.increase_indent @_limit_context_infers def eval_node(context, element): - debug.dbg('eval_node %s@%s', element, element.start_pos) + debug.dbg('eval_node %s@%s in %s', element, element.start_pos, context) evaluator = context.evaluator typ = element.type if typ in ('name', 'number', 'string', 'atom', 'strings', 'keyword'): diff --git a/jedi/plugins/typeshed.py b/jedi/plugins/typeshed.py index 6495ab8a..a67d20ee 100644 --- a/jedi/plugins/typeshed.py +++ b/jedi/plugins/typeshed.py @@ -12,6 +12,7 @@ from jedi.evaluate.filters import ParserTreeFilter, \ NameWrapper, AbstractFilter, TreeNameDefinition from jedi.evaluate.context import ModuleContext, FunctionContext, \ ClassContext +from jedi.evaluate.context.klass import ClassMixin from jedi.evaluate.context.typing import TypingModuleFilterWrapper, \ TypingModuleName from jedi.evaluate.compiled.context import CompiledName @@ -221,7 +222,7 @@ class StubOnlyName(TreeNameDefinition): def infer(self): inferred = super(StubOnlyName, self).infer() return [ - StubOnlyClass(c) if isinstance(c, ClassContext) else c + StubOnlyClass.create_cached(c.evaluator, c) if isinstance(c, ClassContext) else c for c in inferred ] @@ -330,6 +331,13 @@ class StubFilter(AbstractFilter): result.append(StubName(name, stub_name)) return result + def __repr__(self): + return '%s(%s, %s)' % ( + self.__class__.__name__, + self._non_stub_filters, + self._stub_filters, + ) + class _MixedStubContextMixin(object): """ @@ -427,7 +435,7 @@ class StubOnlyModuleContext(_StubOnlyContext, ModuleContext): yield f -class StubOnlyClass(_StubOnlyContext, ContextWrapper): +class StubOnlyClass(_StubOnlyContext, ClassMixin, ContextWrapper): pass