mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-08 06:44:46 +08:00
Fix an issue with global stmts. They caused recursionerrors when used wrong. Fixes #610.
This commit is contained in:
@@ -140,7 +140,7 @@ class Evaluator(object):
|
|||||||
scopes = f.scopes(search_global)
|
scopes = f.scopes(search_global)
|
||||||
if is_goto:
|
if is_goto:
|
||||||
return f.filter_name(scopes)
|
return f.filter_name(scopes)
|
||||||
return f.find(scopes, search_global)
|
return f.find(scopes, attribute_lookup=not search_global)
|
||||||
|
|
||||||
#@memoize_default(default=[], evaluator_is_first_arg=True)
|
#@memoize_default(default=[], evaluator_is_first_arg=True)
|
||||||
#@recursion.recursion_decorator
|
#@recursion.recursion_decorator
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from jedi.evaluate.cache import memoize_default
|
|||||||
from jedi.evaluate import imports
|
from jedi.evaluate import imports
|
||||||
|
|
||||||
|
|
||||||
MAX_PARAM_SEARCHES = 10
|
MAX_PARAM_SEARCHES = 20
|
||||||
|
|
||||||
|
|
||||||
class ParamListener(object):
|
class ParamListener(object):
|
||||||
|
|||||||
@@ -90,27 +90,31 @@ class NameFinder(object):
|
|||||||
self._found_predefined_if_name = None
|
self._found_predefined_if_name = None
|
||||||
|
|
||||||
@debug.increase_indent
|
@debug.increase_indent
|
||||||
def find(self, scopes, search_global=False):
|
def find(self, scopes, attribute_lookup):
|
||||||
|
"""
|
||||||
|
:params bool attribute_lookup: Tell to logic if we're accessing the
|
||||||
|
attribute or the contents of e.g. a function.
|
||||||
|
"""
|
||||||
# TODO rename scopes to names_dicts
|
# TODO rename scopes to names_dicts
|
||||||
|
|
||||||
names = self.filter_name(scopes)
|
names = self.filter_name(scopes)
|
||||||
if self._found_predefined_if_name is not None:
|
if self._found_predefined_if_name is not None:
|
||||||
return self._found_predefined_if_name
|
return self._found_predefined_if_name
|
||||||
|
|
||||||
types = self._names_to_types(names, search_global)
|
types = self._names_to_types(names, attribute_lookup)
|
||||||
|
|
||||||
if not names and not types \
|
if not names and not types \
|
||||||
and not (isinstance(self.name_str, tree.Name)
|
and not (isinstance(self.name_str, tree.Name) and
|
||||||
and isinstance(self.name_str.parent.parent, tree.Param)):
|
isinstance(self.name_str.parent.parent, tree.Param)):
|
||||||
if not isinstance(self.name_str, (str, unicode)): # TODO Remove?
|
if not isinstance(self.name_str, (str, unicode)): # TODO Remove?
|
||||||
if search_global:
|
if attribute_lookup:
|
||||||
|
analysis.add_attribute_error(self._evaluator,
|
||||||
|
self.scope, self.name_str)
|
||||||
|
else:
|
||||||
message = ("NameError: name '%s' is not defined."
|
message = ("NameError: name '%s' is not defined."
|
||||||
% self.name_str)
|
% self.name_str)
|
||||||
analysis.add(self._evaluator, 'name-error', self.name_str,
|
analysis.add(self._evaluator, 'name-error', self.name_str,
|
||||||
message)
|
message)
|
||||||
else:
|
|
||||||
analysis.add_attribute_error(self._evaluator,
|
|
||||||
self.scope, self.name_str)
|
|
||||||
|
|
||||||
debug.dbg('finder._names_to_types: %s -> %s', names, types)
|
debug.dbg('finder._names_to_types: %s -> %s', names, types)
|
||||||
return types
|
return types
|
||||||
@@ -267,7 +271,7 @@ class NameFinder(object):
|
|||||||
result = inst.execute_subscope_by_name('__getattribute__', name)
|
result = inst.execute_subscope_by_name('__getattribute__', name)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _names_to_types(self, names, search_global):
|
def _names_to_types(self, names, attribute_lookup):
|
||||||
types = set()
|
types = set()
|
||||||
|
|
||||||
# Add isinstance and other if/assert knowledge.
|
# Add isinstance and other if/assert knowledge.
|
||||||
@@ -287,7 +291,7 @@ class NameFinder(object):
|
|||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
new_types = _name_to_types(self._evaluator, name, self.scope)
|
new_types = _name_to_types(self._evaluator, name, self.scope)
|
||||||
if isinstance(self.scope, (er.Class, er.Instance)) and not search_global:
|
if isinstance(self.scope, (er.Class, er.Instance)) and attribute_lookup:
|
||||||
types |= set(self._resolve_descriptors(name, new_types))
|
types |= set(self._resolve_descriptors(name, new_types))
|
||||||
else:
|
else:
|
||||||
types |= set(new_types)
|
types |= set(new_types)
|
||||||
@@ -316,8 +320,17 @@ class NameFinder(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _get_global_stmt_scopes(evaluator, global_stmt, name):
|
||||||
|
global_stmt_scope = global_stmt.get_parent_scope()
|
||||||
|
module = global_stmt_scope.get_parent_until()
|
||||||
|
for used_name in module.used_names[str(name)]:
|
||||||
|
if used_name.parent.type == 'global_stmt':
|
||||||
|
yield evaluator.wrap(used_name.get_parent_scope())
|
||||||
|
|
||||||
|
|
||||||
@memoize_default(set(), evaluator_is_first_arg=True)
|
@memoize_default(set(), evaluator_is_first_arg=True)
|
||||||
def _name_to_types(evaluator, name, scope):
|
def _name_to_types(evaluator, name, scope):
|
||||||
|
types = []
|
||||||
typ = name.get_definition()
|
typ = name.get_definition()
|
||||||
if typ.isinstance(tree.ForStmt):
|
if typ.isinstance(tree.ForStmt):
|
||||||
types = pep0484.find_type_from_comment_hint_for(evaluator, typ, name)
|
types = pep0484.find_type_from_comment_hint_for(evaluator, typ, name)
|
||||||
@@ -339,14 +352,14 @@ def _name_to_types(evaluator, name, scope):
|
|||||||
types = evaluator.eval_element(typ.node_from_name(name))
|
types = evaluator.eval_element(typ.node_from_name(name))
|
||||||
elif isinstance(typ, tree.Import):
|
elif isinstance(typ, tree.Import):
|
||||||
types = imports.ImportWrapper(evaluator, name).follow()
|
types = imports.ImportWrapper(evaluator, name).follow()
|
||||||
elif isinstance(typ, tree.GlobalStmt):
|
elif typ.type == 'global_stmt':
|
||||||
# TODO theoretically we shouldn't be using search_global here, it
|
for s in _get_global_stmt_scopes(evaluator, typ, name):
|
||||||
# doesn't make sense, because it's a local search (for that name)!
|
finder = NameFinder(evaluator, s, str(name))
|
||||||
# However, globals are not that important and resolving them doesn't
|
names_dicts = finder.scopes(search_global=True)
|
||||||
# guarantee correctness in any way, because we don't check for when
|
# For global_stmt lookups, we only need the first possible scope,
|
||||||
# something is executed.
|
# which means the function itself.
|
||||||
types = evaluator.find_types(typ.get_parent_scope(), str(name),
|
names_dicts = [next(names_dicts)]
|
||||||
search_global=True)
|
types += finder.find(names_dicts, attribute_lookup=False)
|
||||||
elif isinstance(typ, tree.TryStmt):
|
elif isinstance(typ, tree.TryStmt):
|
||||||
# TODO an exception can also be a tuple. Check for those.
|
# TODO an exception can also be a tuple. Check for those.
|
||||||
# TODO check for types that are not classes and add it to
|
# TODO check for types that are not classes and add it to
|
||||||
|
|||||||
@@ -165,6 +165,17 @@ def global_define():
|
|||||||
#? int()
|
#? int()
|
||||||
global_var_in_func
|
global_var_in_func
|
||||||
|
|
||||||
|
|
||||||
|
def funct1():
|
||||||
|
# From issue #610
|
||||||
|
global global_dict_var
|
||||||
|
global_dict_var = dict()
|
||||||
|
def funct2():
|
||||||
|
global global_dict_var
|
||||||
|
#? dict()
|
||||||
|
global_dict_var
|
||||||
|
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# within docstrs
|
# within docstrs
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|||||||
Reference in New Issue
Block a user