from abc import abstractproperty from jedi.common import unite, to_list from jedi import debug from jedi.evaluate import compiled from jedi.evaluate.filters import ParserTreeFilter, ContextName, TreeNameDefinition from jedi.evaluate.context import Context from jedi.evaluate.cache import memoize_default from jedi.evaluate import representation as er 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): return unite(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_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, 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('__getitem__') except KeyError: debug.warning('No __getitem__, cannot access the array.') return set() else: index_obj = compiled.create(self.evaluator, index) return unite(name.execute_evaluated(index_obj) for name in names) 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) @memoize_default() def create_instance_context(self, class_context, node): scope = node.get_parent_scope() if scope == class_context.classdef: return self else: parent_context = self.create_instance_context(class_context, scope) if scope.type == 'funcdef': if scope.name.value == '__init__' and parent_context == self: return InstanceFunctionExecution( self, class_context.parent_context, scope, self.var_args ) else: return er.AnonymousFunctionExecution( self.evaluator, class_context.parent_context, scope, ) else: raise NotImplementedError return class_context 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 BoundMethod(object): def __init__(self, instance, class_context, function): self._instance = instance self._class_context = class_context self._function = function def __getattr__(self, name): return getattr(self._function, name) def py__call__(self, var_args): function_execution = InstanceFunctionExecution( self._instance, self._class_context.parent_context, self._function.funcdef, var_args ) return self._function.infer_function_execution(function_execution) class InstanceNameDefinition(TreeNameDefinition): def infer(self): contexts = super(InstanceNameDefinition, self).infer() for context in contexts: yield context class InstanceClassFilter(ParserTreeFilter): name_class = InstanceNameDefinition def __init__(self, evaluator, context, class_context, origin_scope): super(InstanceClassFilter, self).__init__( evaluator=evaluator, context=context, parser_scope=class_context.classdef, 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 = 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 [name for name in names if self._access_possible(name)] def _check_flows(self, names): return names def _convert_names(self, names): return [LazyInstanceName(self._context, self._class_context, name) for name in 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 trailer.type == 'trailer' \ and len(trailer.children) == 2 \ and trailer.children[0] == '.': if name.is_definition() and self._access_possible(name): yield name continue init_execution = self._context.get_init_function() # 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 name class LazyInstanceName(TreeNameDefinition): """ This name calculates the parent_context lazily. """ def __init__(self, instance, class_context, name): self._instance = instance self._class_context = class_context self.name = name @property def parent_context(self): return self._instance.create_instance_context(self._class_context, self.name) def infer(self): values = super(LazyInstanceName, self).infer() for v in values: if isinstance(v, er.FunctionContext): yield BoundMethod(self._instance, self._class_context, v) else: yield v class InstanceVarArgs(object): def __init__(self, instance, var_args): self._instance = instance self._var_args = var_args def unpack(self, func=None): yield None, self._instance for values in self._var_args.unpack(func): yield values class InstanceFunctionExecution(er.FunctionExecutionContext): def __init__(self, instance, parent_context, funcdef, var_args): var_args = InstanceVarArgs(instance, var_args) super(InstanceFunctionExecution, self).__init__( instance.evaluator, parent_context, funcdef, var_args)