from abc import abstractproperty from jedi import debug from jedi.evaluate import compiled from jedi.evaluate.filters import ParserTreeFilter, ContextName from jedi.evaluate.context import Context class AbstractInstanceContext(Context): """ This class is used to evaluate instances. """ 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 ##### """" if class_context.name.string_name in ['list', 'set'] \ and evaluator.BUILTINS == parent_context.get_root_context(): # compare the module path with the builtin name. self.var_args = iterable.check_array_instances(evaluator, self) elif not is_generated: # Need to execute the __init__ function, because the dynamic param # searching needs it. try: method = self.get_subscope_by_name('__init__') except KeyError: pass else: self._init_execution = evaluator.execute(method, self.var_args) """ def is_class(self): return False @property def py__call__(self): names = self._get_function_slot_names('__call__') if not names: # Means the Instance is not callable. raise AttributeError def execute(arguments): contexts = set() for name in names: for context in name.infer(): context.execute(arguments) return contexts 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_slot(self, name, *args): raise NotImplementedError method = self.get_subscope_by_name(name) return self._evaluator.execute_evaluated(method, *args) def get_descriptor_returns(self, obj): """ Throws a KeyError if there's no method. """ raise NotImplementedError # Arguments in __get__ descriptors are obj, class. # `method` is the new parent of the array, don't know if that's good. none_obj = compiled.create(self._evaluator, None) args = [obj, obj.base] if isinstance(obj, Instance) else [none_obj, obj] try: return self.execute_subscope_by_name('__get__', *args) except KeyError: return set([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 isinstance(cls, compiled.CompiledObject): yield SelfNameFilter(self._evaluator, self, cls, origin_scope) else: yield SelfNameFilter(self._evaluator, self, cls.base, 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.base, origin_scope) def py__getitem__(self, index): try: method = self.get_subscope_by_name('__getitem__') except KeyError: debug.warning('No __getitem__, cannot access the array.') return set() else: index_obj = compiled.create(self._evaluator, index) return self._evaluator.execute_evaluated(method, index_obj) def py__iter__(self): try: method = self.get_subscope_by_name('__iter__') except KeyError: debug.warning('No __iter__ on %s.' % self) return else: iters = self._evaluator.execute(method) for generator in iters: if isinstance(generator, Instance): # `__next__` logic. name = '__next__' if is_py3 else 'next' try: yield generator.execute_subscope_by_name(name) except KeyError: debug.warning('Instance has no __next__ function in %s.', generator) else: for typ in generator.py__iter__(): yield typ @abstractproperty def name(self): pass def __repr__(self): return "<%s of %s(%s)>" % (type(self).__name__, self._class_context, self.var_args) class CompiledInstance(AbstractInstanceContext): @property def name(self): return compiled.CompiledContextName(self, self._class_context.name.string_name) class TreeInstance(AbstractInstanceContext): @property def name(self): return ContextName(self, self._class_context.name) class CompiledInstanceClassFilter(compiled.CompiledObjectFilter): def __init__(self, evaluator, instance, compiled_object): super(CompiledInstanceClassFilter, self).__init__( evaluator, compiled_object, is_instance=True, ) self._instance = instance def _filter(self, names): names = super(CompiledInstanceClassFilter, self)._filter(names) return [get_instance_el(self._evaluator, self._instance, name, True) for name in names] class InstanceClassFilter(ParserTreeFilter): def __init__(self, evaluator, context, parser_scope, origin_scope): super(InstanceClassFilter, self).__init__( evaluator=evaluator, context=context, parser_scope=parser_scope, origin_scope=origin_scope ) 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 = node.get_parent_scope() 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 [get_instance_el(self._evaluator, self._context, name, True) for name in names if self._access_possible(name)] def _check_flows(self, names): return names class SelfNameFilter(InstanceClassFilter): def _filter(self, names): names = self._filter_self_names(names) if isinstance(self._parser_scope, compiled.CompiledObject): # This would be for builtin skeletons, which are not yet supported. return [] 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 tree.is_node(trailer, 'trailer') \ and len(trailer.children) == 2 \ and trailer.children[0] == '.': if name.is_definition() and self._access_possible(name): init_execution = self._context._get_init_execution() # Hopefully we can somehow change this. if init_execution is not None and \ init_execution.start_pos < name.start_pos < init_execution.end_pos: name = init_execution.name_for_position(name.start_pos) yield get_instance_el(self._evaluator, self._context, name)