Fixing getattr tests.

This commit is contained in:
Dave Halter
2016-11-07 20:15:58 +01:00
parent 40f599c3b6
commit 4a8fd73601
9 changed files with 83 additions and 66 deletions

View File

@@ -78,7 +78,7 @@ from jedi.evaluate import precedence
from jedi.evaluate import param from jedi.evaluate import param
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi.evaluate.context import Context from jedi.evaluate.context import Context
from jedi.evaluate.instance import AnonymousInstance from jedi.evaluate.instance import AnonymousInstance, AnonymousInstanceFunctionExecution
class Evaluator(object): class Evaluator(object):
@@ -527,7 +527,14 @@ class Evaluator(object):
if scope_node == module_context.module_node: if scope_node == module_context.module_node:
return module_context return module_context
elif is_funcdef: 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': elif scope_node.type == 'classdef':
class_context = er.ClassContext(self, scope_node, parent_context) class_context = er.ClassContext(self, scope_node, parent_context)
if child_is_funcdef: if child_is_funcdef:

View File

@@ -1,4 +1,6 @@
from jedi.common import unite from jedi.common import unite
class Context(object): class Context(object):
type = None # TODO remove type = None # TODO remove

View File

@@ -98,9 +98,9 @@ def _search_function_executions(evaluator, funcdef):
""" """
from jedi.evaluate import representation as er from jedi.evaluate import representation as er
def get_possible_nodes(module, func_name): def get_possible_nodes(module_node, func_name):
try: try:
names = module.used_names[func_name] names = module_node.used_names[func_name]
except KeyError: except KeyError:
return return
@@ -110,7 +110,7 @@ def _search_function_executions(evaluator, funcdef):
if trailer.type == 'trailer' and bracket == '(': if trailer.type == 'trailer' and bracket == '(':
yield name, trailer yield name, trailer
current_module = funcdef.get_parent_until() current_module_node = funcdef.get_parent_until()
func_name = unicode(funcdef.name) func_name = unicode(funcdef.name)
compare_node = funcdef compare_node = funcdef
if func_name == '__init__': if func_name == '__init__':
@@ -122,9 +122,10 @@ def _search_function_executions(evaluator, funcdef):
found_executions = False found_executions = False
i = 0 i = 0
for mod in imports.get_modules_containing_name(evaluator, [current_module], func_name): for module_node in imports.get_module_nodes_containing_name(
module_context = er.ModuleContext(evaluator, mod) evaluator, [current_module_node], func_name):
for name, trailer in get_possible_nodes(mod, func_name): module_context = er.ModuleContext(evaluator, module_node)
for name, trailer in get_possible_nodes(module_node, func_name):
i += 1 i += 1
# This is a simple way to stop Jedi's dynamic param recursion # This is a simple way to stop Jedi's dynamic param recursion
@@ -134,16 +135,16 @@ def _search_function_executions(evaluator, funcdef):
return return
random_context = evaluator.create_context(module_context, name) random_context = evaluator.create_context(module_context, name)
for context in evaluator.goto_definitions(random_context, name): for value in evaluator.goto_definitions(random_context, name):
if compare_node == context.funcdef: if compare_node == value.funcdef:
arglist = trailer.children[1] arglist = trailer.children[1]
if arglist == ')': if arglist == ')':
arglist = () arglist = ()
args = TreeArguments(evaluator, random_context, arglist, trailer) args = TreeArguments(evaluator, random_context, arglist, trailer)
yield er.FunctionExecutionContext( yield er.FunctionExecutionContext(
evaluator, evaluator,
context.parent_context, value.parent_context,
context.funcdef, value.funcdef,
args args
) )
found_executions = True found_executions = True

View File

@@ -75,6 +75,16 @@ class ParamName(ContextName):
return params[self.tree_name.parent.position_nr] 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): class AbstractFilter(object):
_until_position = None _until_position = None
@@ -145,6 +155,8 @@ class ParserTreeFilter(AbstractUsedNamesFilter):
class FunctionExecutionFilter(ParserTreeFilter): class FunctionExecutionFilter(ParserTreeFilter):
param_name = ParamName
def __init__(self, evaluator, context, parser_scope, def __init__(self, evaluator, context, parser_scope,
until_position=None, origin_scope=None): until_position=None, origin_scope=None):
super(FunctionExecutionFilter, self).__init__( super(FunctionExecutionFilter, self).__init__(
@@ -160,11 +172,15 @@ class FunctionExecutionFilter(ParserTreeFilter):
for name in names: for name in names:
param = search_ancestor(name, 'param') param = search_ancestor(name, 'param')
if param: if param:
yield ParamName(self._context, name) yield self.param_name(self._context, name)
else: else:
yield TreeNameDefinition(self._context, name) yield TreeNameDefinition(self._context, name)
class AnonymousInstanceFunctionExecutionFilter(FunctionExecutionFilter):
param_name = AnonymousInstanceParamName
class GlobalNameFilter(AbstractUsedNamesFilter): class GlobalNameFilter(AbstractUsedNamesFilter):
def __init__(self, context, parser_scope, origin_scope=None): def __init__(self, context, parser_scope, origin_scope=None):
super(GlobalNameFilter, self).__init__(context, parser_scope) super(GlobalNameFilter, self).__init__(context, parser_scope)

View File

@@ -448,16 +448,15 @@ def _load_module(evaluator, path=None, source=None, sys_path=None, parent_module
p = path p = path
p = fast.FastParser(evaluator.grammar, common.source_to_unicode(source), p) p = fast.FastParser(evaluator.grammar, common.source_to_unicode(source), p)
save_parser(path, p) save_parser(path, p)
from jedi.evaluate.representation import ModuleWrapper return p.module
return ModuleWrapper(evaluator, p.module, parent_module)
if sys_path is None: if sys_path is None:
sys_path = evaluator.sys_path sys_path = evaluator.sys_path
cached = load_parser(path) cached = load_parser(path)
module = load(source) if cached is None else cached.module module = load(source) if cached is None else cached.module
module = evaluator.wrap(module) from jedi.evaluate.representation import ModuleContext
return module return ModuleContext(evaluator, module)
def add_module(evaluator, module_name, module): def add_module(evaluator, module_name, module):
@@ -469,7 +468,7 @@ def add_module(evaluator, module_name, module):
evaluator.modules[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. Search a name in the directories of modules.
""" """
@@ -492,9 +491,9 @@ def get_modules_containing_name(evaluator, mods, name):
return module return module
# skip non python modules # 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() mod_paths = set()
for m in mods: for m in module_nodes:
mod_paths.add(m.path) mod_paths.add(m.path)
yield m yield m
@@ -513,5 +512,5 @@ def get_modules_containing_name(evaluator, mods, name):
for p in sorted(paths): for p in sorted(paths):
# make testing easier, sort it - same results on every interpreter # make testing easier, sort it - same results on every interpreter
c = check_python_file(p) c = check_python_file(p)
if c is not None and c not in mods and not isinstance(c, compiled.CompiledObject): if c is not None and c not in module_nodes and not isinstance(c, compiled.CompiledObject):
yield c yield c.module_node

View File

@@ -3,7 +3,7 @@ from abc import abstractproperty
from jedi.common import unite from jedi.common import unite
from jedi import debug from jedi import debug
from jedi.evaluate import compiled 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.context import Context, LazyKnownContext
from jedi.evaluate.cache import memoize_default from jedi.evaluate.cache import memoize_default
from jedi.cache import memoize_method from jedi.cache import memoize_method
@@ -155,7 +155,7 @@ class CompiledInstance(AbstractInstanceContext):
class TreeInstance(AbstractInstanceContext): class TreeInstance(AbstractInstanceContext):
@property @property
def name(self): def name(self):
return ContextName(self, self.class_context.name.tree_name) return filters.ContextName(self, self.class_context.name.tree_name)
@memoize_default() @memoize_default()
def create_instance_context(self, class_context, node): def create_instance_context(self, class_context, node):
@@ -230,14 +230,14 @@ class BoundMethod(object):
return '<%s: %s>' % (self.__class__.__name__, self._function) return '<%s: %s>' % (self.__class__.__name__, self._function)
class InstanceNameDefinition(TreeNameDefinition): class InstanceNameDefinition(filters.TreeNameDefinition):
def infer(self): def infer(self):
contexts = super(InstanceNameDefinition, self).infer() contexts = super(InstanceNameDefinition, self).infer()
for context in contexts: for context in contexts:
yield context yield context
class InstanceClassFilter(ParserTreeFilter): class InstanceClassFilter(filters.ParserTreeFilter):
name_class = InstanceNameDefinition name_class = InstanceNameDefinition
def __init__(self, evaluator, context, class_context, origin_scope): def __init__(self, evaluator, context, class_context, origin_scope):
@@ -298,7 +298,7 @@ class SelfNameFilter(InstanceClassFilter):
yield name yield name
class LazyInstanceName(TreeNameDefinition): class LazyInstanceName(filters.TreeNameDefinition):
""" """
This name calculates the parent_context lazily. This name calculates the parent_context lazily.
""" """
@@ -321,19 +321,25 @@ class LazyInstanceName(TreeNameDefinition):
class InstanceVarArgs(object): class InstanceVarArgs(object):
def __init__(self, instance, var_args): def __init__(self, instance, funcdef, var_args):
self._instance = instance self._instance = instance
self._funcdef = funcdef
self._var_args = var_args self._var_args = var_args
@memoize_method @memoize_method
def get_var_args(self): def _get_var_args(self):
if self._var_args is None: 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 return self._var_args
def unpack(self, func=None): def unpack(self, func=None):
yield None, LazyKnownContext(self._instance) 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 yield values
def get_calling_var_args(self): def get_calling_var_args(self):
@@ -342,7 +348,15 @@ class InstanceVarArgs(object):
class InstanceFunctionExecution(er.FunctionExecutionContext): class InstanceFunctionExecution(er.FunctionExecutionContext):
def __init__(self, instance, parent_context, funcdef, var_args): 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__( super(InstanceFunctionExecution, self).__init__(
instance.evaluator, parent_context, funcdef, var_args) 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)

View File

@@ -28,35 +28,20 @@ def try_iter_content(types, depth=0):
try_iter_content(iter_types, depth + 1) try_iter_content(iter_types, depth + 1)
class AbstractArguments(tree.Base): class AbstractArguments():
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)
def eval_argument_clinic(self, arguments): def eval_argument_clinic(self, arguments):
"""Uses a list with argument clinic information (see PEP 436).""" """Uses a list with argument clinic information (see PEP 436)."""
raise DeprecationWarning('not sure if we really deprecate it')
iterator = self.unpack() iterator = self.unpack()
for i, (name, optional, allow_kwargs) in enumerate(arguments): 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: if key is not None:
raise NotImplementedError 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', debug.warning('TypeError: %s expected at least %s arguments, got %s',
name, len(arguments), i) name, len(arguments), i)
raise ValueError raise ValueError
values = set(chain.from_iterable(self._evaluator.eval_element(self._context, el) values = set() if argument is None else argument.infer()
for el in va_values))
if not values and not optional: if not values and not optional:
# For the stdlib we always want values. If we don't get them, # For the stdlib we always want values. If we don't get them,
# that's ok, maybe something is too hard to resolve, however, # that's ok, maybe something is too hard to resolve, however,
@@ -65,11 +50,6 @@ class AbstractArguments(tree.Base):
raise ValueError raise ValueError
yield values 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): def eval_args(self):
# TODO this method doesn't work with named args and a lot of other # TODO this method doesn't work with named args and a lot of other
# things. Use unpack. # things. Use unpack.
@@ -171,7 +151,7 @@ class TreeArguments(AbstractArguments):
yield argument, default, stars yield argument, default, stars
def __repr__(self): 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): def get_calling_var_args(self):
return _get_calling_var_args(self._evaluator, self) return _get_calling_var_args(self._evaluator, self)

View File

@@ -589,6 +589,8 @@ class FunctionExecutionContext(Executed):
multiple calls to functions and recursion has to be avoided. But this is multiple calls to functions and recursion has to be avoided. But this is
responsibility of the decorators. responsibility of the decorators.
""" """
function_execution_filter = FunctionExecutionFilter
def __init__(self, evaluator, parent_context, funcdef, var_args): def __init__(self, evaluator, parent_context, funcdef, var_args):
super(FunctionExecutionContext, self).__init__(evaluator, parent_context, var_args) super(FunctionExecutionContext, self).__init__(evaluator, parent_context, var_args)
self.funcdef = funcdef self.funcdef = funcdef
@@ -707,9 +709,9 @@ class FunctionExecutionContext(Executed):
del evaluator.predefined_if_name_dict_dict[for_stmt] del evaluator.predefined_if_name_dict_dict[for_stmt]
def get_filters(self, search_global, until_position=None, origin_scope=None): def get_filters(self, search_global, until_position=None, origin_scope=None):
yield FunctionExecutionFilter(self.evaluator, self, self.funcdef, yield self.function_execution_filter(self.evaluator, self, self.funcdef,
until_position, until_position,
origin_scope=origin_scope) origin_scope=origin_scope)
@memoize_default(default=NO_DEFAULT) @memoize_default(default=NO_DEFAULT)
def get_params(self): def get_params(self):

View File

@@ -31,7 +31,7 @@ class NotInStdLib(LookupError):
def execute(evaluator, obj, arguments): def execute(evaluator, obj, arguments):
try: try:
obj_name = str(obj.name) obj_name = obj.name.string_name
except AttributeError: except AttributeError:
pass pass
else: 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): def builtins_getattr(evaluator, objects, names, defaults=None):
# follow the first param # follow the first param
for obj in objects: 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: for name in names:
if precedence.is_string(name): if precedence.is_string(name):
return evaluator.find_types(obj, name.obj) return evaluator.find_types(obj, name.obj)