from abc import abstractproperty from jedi import debug from jedi.evaluate import compiled from jedi.evaluate import filters from jedi.evaluate.base_context import Context, NO_CONTEXTS, ContextSet, \ iterator_to_context_set from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments from jedi.cache import memoize_method from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, AbstractFunction from jedi.evaluate.context.klass import ClassContext, apply_py__get__ from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope class BaseInstanceFunctionExecution(FunctionExecutionContext): def __init__(self, instance, *args, **kwargs): self.instance = instance super(BaseInstanceFunctionExecution, self).__init__( instance.evaluator, *args, **kwargs) class InstanceFunctionExecution(BaseInstanceFunctionExecution): def __init__(self, instance, parent_context, function_context, var_args): var_args = InstanceVarArgs(self, var_args) super(InstanceFunctionExecution, self).__init__( instance, parent_context, function_context, var_args) class AnonymousInstanceFunctionExecution(BaseInstanceFunctionExecution): function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter def __init__(self, instance, parent_context, function_context, var_args): super(AnonymousInstanceFunctionExecution, self).__init__( instance, parent_context, function_context, var_args) class AbstractInstanceContext(Context): """ This class is used to evaluate instances. """ api_type = u'instance' function_execution_cls = InstanceFunctionExecution def __init__(self, evaluator, parent_context, class_context, var_args): super(AbstractInstanceContext, self).__init__(evaluator, parent_context) # Generated instances are classes that are just generated by self # (No var_args) used. self.class_context = class_context self.var_args = var_args def is_class(self): return False @property def py__call__(self): names = self.get_function_slot_names(u'__call__') if not names: # Means the Instance is not callable. raise AttributeError def execute(arguments): return ContextSet.from_sets(name.execute(arguments) for name in names) return execute def py__class__(self): return self.class_context def py__bool__(self): # Signalize that we don't know about the bool type. return None def get_function_slot_names(self, name): # Python classes don't look at the dictionary of the instance when # looking up `__call__`. This is something that has to do with Python's # internal slot system (note: not __slots__, but C slots). for filter in self.get_filters(include_self_names=False): names = filter.get(name) if names: return names return [] def execute_function_slots(self, names, *evaluated_args): return ContextSet.from_sets( name.execute_evaluated(*evaluated_args) for name in names ) def py__get__(self, obj): # Arguments in __get__ descriptors are obj, class. # `method` is the new parent of the array, don't know if that's good. names = self.get_function_slot_names(u'__get__') if names: if isinstance(obj, AbstractInstanceContext): return self.execute_function_slots(names, obj, obj.class_context) else: none_obj = compiled.builtin_from_name(self.evaluator, u'None') return self.execute_function_slots(names, none_obj, obj) else: return ContextSet(self) def get_filters(self, search_global=None, until_position=None, origin_scope=None, include_self_names=True): if include_self_names: for cls in self.class_context.py__mro__(): if not isinstance(cls, compiled.CompiledObject) \ or cls.tree_node is not None: # In this case we're excluding compiled objects that are # not fake objects. It doesn't make sense for normal # compiled objects to search for self variables. yield SelfAttributeFilter(self.evaluator, self, cls, origin_scope) for cls in self.class_context.py__mro__(): if isinstance(cls, compiled.CompiledObject): yield CompiledInstanceClassFilter(self.evaluator, self, cls) else: yield InstanceClassFilter(self.evaluator, self, cls, origin_scope) def py__getitem__(self, index): try: names = self.get_function_slot_names(u'__getitem__') except KeyError: debug.warning('No __getitem__, cannot access the array.') return NO_CONTEXTS else: index_obj = compiled.create_simple_object(self.evaluator, index) return self.execute_function_slots(names, index_obj) def py__iter__(self): iter_slot_names = self.get_function_slot_names(u'__iter__') if not iter_slot_names: debug.warning('No __iter__ on %s.' % self) return for generator in self.execute_function_slots(iter_slot_names): if isinstance(generator, AbstractInstanceContext): # `__next__` logic. if self.evaluator.environment.version_info.major == 2: name = u'next' else: name = u'__next__' iter_slot_names = generator.get_function_slot_names(name) if iter_slot_names: yield LazyKnownContexts( generator.execute_function_slots(iter_slot_names) ) else: debug.warning('Instance has no __next__ function in %s.', generator) else: for lazy_context in generator.py__iter__(): yield lazy_context @abstractproperty def name(self): pass def _create_init_execution(self, class_context, bound_method): return self.function_execution_cls( self, class_context.parent_context, bound_method, self.var_args ) def create_init_executions(self): for name in self.get_function_slot_names(u'__init__'): if isinstance(name, SelfName): function = FunctionContext.from_context( self.parent_context, name.tree_name.parent ) bound_method = BoundMethod(self, name.class_context, function) yield self._create_init_execution(name.class_context, bound_method) @evaluator_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 = FunctionContext.from_context( parent_context, scope, ) bound_method = BoundMethod(self, class_context, func) if scope.name.value == '__init__' and parent_context == class_context: return self._create_init_execution(class_context, bound_method) else: return bound_method.get_function_execution() elif scope.type == 'classdef': class_context = ClassContext(self.evaluator, parent_context, scope) return class_context elif scope.type == '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 __repr__(self): return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_context, self.var_args) class CompiledInstance(AbstractInstanceContext): def __init__(self, *args, **kwargs): super(CompiledInstance, self).__init__(*args, **kwargs) # I don't think that dynamic append lookups should happen here. That # sounds more like something that should go to py__iter__. self._original_var_args = self.var_args if self.class_context.name.string_name in ['list', 'set'] \ and self.parent_context.get_root_context() == self.evaluator.builtins_module: # compare the module path with the builtin name. self.var_args = iterable.get_dynamic_array_instance(self) @property def name(self): return compiled.CompiledContextName(self, self.class_context.name.string_name) def create_instance_context(self, class_context, node): if get_parent_scope(node).type == 'classdef': return class_context else: return super(CompiledInstance, self).create_instance_context(class_context, node) def get_first_non_keyword_argument_contexts(self): key, lazy_context = next(self._original_var_args.unpack(), ('', None)) if key is not None: return NO_CONTEXTS return lazy_context.infer() class TreeInstance(AbstractInstanceContext): def __init__(self, evaluator, parent_context, class_context, var_args): super(TreeInstance, self).__init__(evaluator, parent_context, class_context, var_args) self.tree_node = class_context.tree_node @property def name(self): return filters.ContextName(self, self.class_context.name.tree_name) class AnonymousInstance(TreeInstance): function_execution_cls = AnonymousInstanceFunctionExecution def __init__(self, evaluator, parent_context, class_context): super(AnonymousInstance, self).__init__( evaluator, parent_context, class_context, var_args=AnonymousArguments(), ) class CompiledInstanceName(compiled.CompiledName): def __init__(self, evaluator, instance, parent_context, name): super(CompiledInstanceName, self).__init__(evaluator, parent_context, name) self._instance = instance @iterator_to_context_set def infer(self): for result_context in super(CompiledInstanceName, self).infer(): is_function = result_context.api_type == 'function' if result_context.tree_node is not None and is_function: yield BoundMethod(self._instance, self.parent_context, result_context) else: if is_function: yield CompiledBoundMethod(result_context) else: yield result_context class CompiledInstanceClassFilter(compiled.CompiledObjectFilter): name_class = CompiledInstanceName def __init__(self, evaluator, instance, compiled_object): super(CompiledInstanceClassFilter, self).__init__( evaluator, compiled_object, is_instance=True, ) self._instance = instance def _create_name(self, name): return self.name_class( self._evaluator, self._instance, self._compiled_object, name) class BoundMethod(AbstractFunction): def __init__(self, instance, klass, function): super(BoundMethod, self).__init__( function.evaluator, function.parent_context, function.tree_node, ) self._instance = instance self._class = klass self._function = function def py__class__(self): return compiled.get_special_object(self.evaluator, u'BOUND_METHOD_CLASS') def get_function_execution(self, arguments=None): if arguments is None: arguments = AnonymousArguments() return AnonymousInstanceFunctionExecution( self._instance, self.parent_context, self, arguments) else: return InstanceFunctionExecution( self._instance, self.parent_context, self, arguments) class CompiledBoundMethod(compiled.CompiledObject): def __init__(self, func): super(CompiledBoundMethod, self).__init__( func.evaluator, func.access_handle, func.parent_context, func.tree_node) def get_param_names(self): return list(super(CompiledBoundMethod, self).get_param_names())[1:] class InstanceNameDefinition(filters.TreeNameDefinition): def infer(self): return super(InstanceNameDefinition, self).infer() class SelfName(filters.TreeNameDefinition): """ This name calculates the parent_context lazily. """ def __init__(self, instance, class_context, tree_name): self._instance = instance self.class_context = class_context self.tree_name = tree_name @property def parent_context(self): return self._instance.create_instance_context(self.class_context, self.tree_name) class LazyInstanceClassName(SelfName): @iterator_to_context_set def infer(self): for result_context in super(LazyInstanceClassName, self).infer(): if isinstance(result_context, FunctionContext): # Classes are never used to resolve anything within the # functions. Only other functions and modules will resolve # those things. yield BoundMethod(self._instance, self.class_context, result_context) else: for c in apply_py__get__(result_context, self._instance): yield c class InstanceClassFilter(filters.ParserTreeFilter): name_class = LazyInstanceClassName def __init__(self, evaluator, context, class_context, origin_scope): super(InstanceClassFilter, self).__init__( evaluator=evaluator, context=context, node_context=class_context, origin_scope=origin_scope ) 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: # This would be for builtin skeletons, which are not yet supported. return list(names) else: start, end = self._parser_scope.start_pos, self._parser_scope.end_pos return [n for n in names if start < n.start_pos < end] def _filter_self_names(self, names): for name in names: trailer = name.parent if trailer.type == 'trailer' \ and len(trailer.children) == 2 \ and trailer.children[0] == '.': if name.is_definition() and self._access_possible(name): yield name def _check_flows(self, names): return names class InstanceVarArgs(AbstractArguments): def __init__(self, execution_context, var_args): self._execution_context = execution_context self._var_args = var_args @memoize_method def _get_var_args(self): return self._var_args @property def argument_node(self): return self._var_args.argument_node @property def trailer(self): return self._var_args.trailer def unpack(self, func=None): yield None, LazyKnownContext(self._execution_context.instance) for values in self._get_var_args().unpack(func): yield values def get_calling_nodes(self): return self._get_var_args().get_calling_nodes()