diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index df7eecb2..876f642a 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -78,7 +78,7 @@ from jedi.evaluate import precedence from jedi.evaluate import param from jedi.evaluate import helpers from jedi.evaluate.context import Context -from jedi.evaluate.instance import AnonymousInstance +from jedi.evaluate.instance import AnonymousInstance, AnonymousInstanceFunctionExecution class Evaluator(object): @@ -527,7 +527,14 @@ class Evaluator(object): if scope_node == module_context.module_node: return module_context elif is_funcdef: - return er.AnonymousFunctionExecution(self, parent_context, scope_node) + if isinstance(parent_context, AnonymousInstance): + return AnonymousInstanceFunctionExecution( + parent_context, + parent_context.parent_context, + scope_node + ) + else: + return er.AnonymousFunctionExecution(self, parent_context, scope_node) elif scope_node.type == 'classdef': class_context = er.ClassContext(self, scope_node, parent_context) if child_is_funcdef: diff --git a/jedi/evaluate/context.py b/jedi/evaluate/context.py index 2dc129f4..fffc81c6 100644 --- a/jedi/evaluate/context.py +++ b/jedi/evaluate/context.py @@ -1,4 +1,6 @@ from jedi.common import unite + + class Context(object): type = None # TODO remove diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index 8fac3ece..7c1887d9 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -98,9 +98,9 @@ def _search_function_executions(evaluator, funcdef): """ from jedi.evaluate import representation as er - def get_possible_nodes(module, func_name): + def get_possible_nodes(module_node, func_name): try: - names = module.used_names[func_name] + names = module_node.used_names[func_name] except KeyError: return @@ -110,7 +110,7 @@ def _search_function_executions(evaluator, funcdef): if trailer.type == 'trailer' and bracket == '(': yield name, trailer - current_module = funcdef.get_parent_until() + current_module_node = funcdef.get_parent_until() func_name = unicode(funcdef.name) compare_node = funcdef if func_name == '__init__': @@ -122,9 +122,10 @@ def _search_function_executions(evaluator, funcdef): found_executions = False i = 0 - for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name): - module_context = er.ModuleContext(evaluator, mod) - for name, trailer in get_possible_nodes(mod, func_name): + for module_node in imports.get_module_nodes_containing_name( + evaluator, [current_module_node], func_name): + module_context = er.ModuleContext(evaluator, module_node) + for name, trailer in get_possible_nodes(module_node, func_name): i += 1 # This is a simple way to stop Jedi's dynamic param recursion @@ -134,16 +135,16 @@ def _search_function_executions(evaluator, funcdef): return random_context = evaluator.create_context(module_context, name) - for context in evaluator.goto_definitions(random_context, name): - if compare_node == context.funcdef: + for value in evaluator.goto_definitions(random_context, name): + if compare_node == value.funcdef: arglist = trailer.children[1] if arglist == ')': arglist = () args = TreeArguments(evaluator, random_context, arglist, trailer) yield er.FunctionExecutionContext( evaluator, - context.parent_context, - context.funcdef, + value.parent_context, + value.funcdef, args ) found_executions = True diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 552bfe37..536457be 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -75,6 +75,16 @@ class ParamName(ContextName): return params[self.tree_name.parent.position_nr] +class AnonymousInstanceParamName(ParamName): + def infer(self): + if self.tree_name.parent.position_nr == 0: + # This is a speed optimization, to return the self param (because + # it's known). This only affects anonymous instances. + return set([self.parent_context]) + else: + return self._get_param().infer() + + class AbstractFilter(object): _until_position = None @@ -145,6 +155,8 @@ class ParserTreeFilter(AbstractUsedNamesFilter): class FunctionExecutionFilter(ParserTreeFilter): + param_name = ParamName + def __init__(self, evaluator, context, parser_scope, until_position=None, origin_scope=None): super(FunctionExecutionFilter, self).__init__( @@ -160,11 +172,15 @@ class FunctionExecutionFilter(ParserTreeFilter): for name in names: param = search_ancestor(name, 'param') if param: - yield ParamName(self._context, name) + yield self.param_name(self._context, name) else: yield TreeNameDefinition(self._context, name) +class AnonymousInstanceFunctionExecutionFilter(FunctionExecutionFilter): + param_name = AnonymousInstanceParamName + + class GlobalNameFilter(AbstractUsedNamesFilter): def __init__(self, context, parser_scope, origin_scope=None): super(GlobalNameFilter, self).__init__(context, parser_scope) diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index d65d897b..50247c6f 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -448,16 +448,15 @@ def _load_module(evaluator, path=None, source=None, sys_path=None, parent_module p = path p = fast.FastParser(evaluator.grammar, common.source_to_unicode(source), p) save_parser(path, p) - from jedi.evaluate.representation import ModuleWrapper - return ModuleWrapper(evaluator, p.module, parent_module) + return p.module if sys_path is None: sys_path = evaluator.sys_path cached = load_parser(path) module = load(source) if cached is None else cached.module - module = evaluator.wrap(module) - return module + from jedi.evaluate.representation import ModuleContext + return ModuleContext(evaluator, module) def add_module(evaluator, module_name, module): @@ -469,7 +468,7 @@ def add_module(evaluator, module_name, module): evaluator.modules[module_name] = module -def get_modules_containing_name(evaluator, mods, name): +def get_module_nodes_containing_name(evaluator, module_nodes, name): """ Search a name in the directories of modules. """ @@ -492,9 +491,9 @@ def get_modules_containing_name(evaluator, mods, name): return module # skip non python modules - mods = set(m for m in mods if not isinstance(m, compiled.CompiledObject)) + module_nodes = set(m for m in module_nodes if not isinstance(m, compiled.CompiledObject)) mod_paths = set() - for m in mods: + for m in module_nodes: mod_paths.add(m.path) yield m @@ -513,5 +512,5 @@ def get_modules_containing_name(evaluator, mods, name): for p in sorted(paths): # make testing easier, sort it - same results on every interpreter c = check_python_file(p) - if c is not None and c not in mods and not isinstance(c, compiled.CompiledObject): - yield c + if c is not None and c not in module_nodes and not isinstance(c, compiled.CompiledObject): + yield c.module_node diff --git a/jedi/evaluate/instance.py b/jedi/evaluate/instance.py index 7ca87fe3..2b84c859 100644 --- a/jedi/evaluate/instance.py +++ b/jedi/evaluate/instance.py @@ -3,7 +3,7 @@ from abc import abstractproperty from jedi.common import unite from jedi import debug from jedi.evaluate import compiled -from jedi.evaluate.filters import ParserTreeFilter, ContextName, TreeNameDefinition +from jedi.evaluate import filters from jedi.evaluate.context import Context, LazyKnownContext from jedi.evaluate.cache import memoize_default from jedi.cache import memoize_method @@ -155,7 +155,7 @@ class CompiledInstance(AbstractInstanceContext): class TreeInstance(AbstractInstanceContext): @property def name(self): - return ContextName(self, self.class_context.name.tree_name) + return filters.ContextName(self, self.class_context.name.tree_name) @memoize_default() def create_instance_context(self, class_context, node): @@ -230,14 +230,14 @@ class BoundMethod(object): return '<%s: %s>' % (self.__class__.__name__, self._function) -class InstanceNameDefinition(TreeNameDefinition): +class InstanceNameDefinition(filters.TreeNameDefinition): def infer(self): contexts = super(InstanceNameDefinition, self).infer() for context in contexts: yield context -class InstanceClassFilter(ParserTreeFilter): +class InstanceClassFilter(filters.ParserTreeFilter): name_class = InstanceNameDefinition def __init__(self, evaluator, context, class_context, origin_scope): @@ -298,7 +298,7 @@ class SelfNameFilter(InstanceClassFilter): yield name -class LazyInstanceName(TreeNameDefinition): +class LazyInstanceName(filters.TreeNameDefinition): """ This name calculates the parent_context lazily. """ @@ -321,19 +321,25 @@ class LazyInstanceName(TreeNameDefinition): class InstanceVarArgs(object): - def __init__(self, instance, var_args): + def __init__(self, instance, funcdef, var_args): self._instance = instance + self._funcdef = funcdef self._var_args = var_args @memoize_method - def get_var_args(self): + def _get_var_args(self): if self._var_args is None: - return search_params(self.evaluator, self.parent_context, self.funcdef) + # TODO this parent_context might be wrong. test?! + return search_params( + self._instance.evaluator, + self._instance.class_context, + self._funcdef + ) return self._var_args def unpack(self, func=None): yield None, LazyKnownContext(self._instance) - for values in self._get_var_args.unpack(func): + for values in self._get_var_args().unpack(func): yield values def get_calling_var_args(self): @@ -342,7 +348,15 @@ class InstanceVarArgs(object): class InstanceFunctionExecution(er.FunctionExecutionContext): def __init__(self, instance, parent_context, funcdef, var_args): - var_args = InstanceVarArgs(instance, var_args) + var_args = InstanceVarArgs(instance, funcdef, var_args) super(InstanceFunctionExecution, self).__init__( instance.evaluator, parent_context, funcdef, var_args) + + +class AnonymousInstanceFunctionExecution(InstanceFunctionExecution): + function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter + + def __init__(self, instance, parent_context, funcdef): + super(AnonymousInstanceFunctionExecution, self).__init__( + instance, parent_context, funcdef, None) diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 0f32e4ee..8c3a70bc 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -28,35 +28,20 @@ def try_iter_content(types, depth=0): try_iter_content(iter_types, depth + 1) -class AbstractArguments(tree.Base): - def get_parent_until(self, *args, **kwargs): - raise DeprecationWarning - if self.trailer is None: - try: - element = self.argument_node[0] - if isinstance(element, iterable.AlreadyEvaluated): - element = list(self._evaluator.eval_element(self._context, element))[0] - except IndexError: - return None - else: - return element.get_parent_until(*args, **kwargs) - else: - return self.trailer.get_parent_until(*args, **kwargs) - +class AbstractArguments(): def eval_argument_clinic(self, arguments): """Uses a list with argument clinic information (see PEP 436).""" - raise DeprecationWarning('not sure if we really deprecate it') iterator = self.unpack() for i, (name, optional, allow_kwargs) in enumerate(arguments): - key, va_values = next(iterator, (None, [])) + key, argument = next(iterator, (None, None)) if key is not None: raise NotImplementedError - if not va_values and not optional: + if argument is None and not optional: debug.warning('TypeError: %s expected at least %s arguments, got %s', name, len(arguments), i) raise ValueError - values = set(chain.from_iterable(self._evaluator.eval_element(self._context, el) - for el in va_values)) + values = set() if argument is None else argument.infer() + if not values and not optional: # For the stdlib we always want values. If we don't get them, # that's ok, maybe something is too hard to resolve, however, @@ -65,11 +50,6 @@ class AbstractArguments(tree.Base): raise ValueError yield values - def scope(self): - raise DeprecationWarning - # Returns the scope in which the arguments are used. - return (self.trailer or self.argument_node).get_parent_until(tree.IsScope) - def eval_args(self): # TODO this method doesn't work with named args and a lot of other # things. Use unpack. @@ -171,7 +151,7 @@ class TreeArguments(AbstractArguments): yield argument, default, stars def __repr__(self): - return '<%s: %s>' % (type(self).__name__, self.argument_node) + return '<%s: %s>' % (self.__class__.__name__, self.argument_node) def get_calling_var_args(self): return _get_calling_var_args(self._evaluator, self) diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index b53d94eb..fdd61487 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -589,6 +589,8 @@ class FunctionExecutionContext(Executed): multiple calls to functions and recursion has to be avoided. But this is responsibility of the decorators. """ + function_execution_filter = FunctionExecutionFilter + def __init__(self, evaluator, parent_context, funcdef, var_args): super(FunctionExecutionContext, self).__init__(evaluator, parent_context, var_args) self.funcdef = funcdef @@ -707,9 +709,9 @@ class FunctionExecutionContext(Executed): del evaluator.predefined_if_name_dict_dict[for_stmt] def get_filters(self, search_global, until_position=None, origin_scope=None): - yield FunctionExecutionFilter(self.evaluator, self, self.funcdef, - until_position, - origin_scope=origin_scope) + yield self.function_execution_filter(self.evaluator, self, self.funcdef, + until_position, + origin_scope=origin_scope) @memoize_default(default=NO_DEFAULT) def get_params(self): diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index 2c286e04..17361952 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -31,7 +31,7 @@ class NotInStdLib(LookupError): def execute(evaluator, obj, arguments): try: - obj_name = str(obj.name) + obj_name = obj.name.string_name except AttributeError: pass else: @@ -109,10 +109,6 @@ def argument_clinic(string, want_obj=False, want_scope=False, want_arguments=Fal def builtins_getattr(evaluator, objects, names, defaults=None): # follow the first param for obj in objects: - if not isinstance(obj, (er.Instance, er.Class, tree.Module, compiled.CompiledObject)): - debug.warning('getattr called without instance') - continue - for name in names: if precedence.is_string(name): return evaluator.find_types(obj, name.obj)