forked from VimPlug/jedi
Merge remote-tracking branch 'upstream/dev' into fix_runtime_error
Conflicts: jedi/evaluate/imports.py
This commit is contained in:
+14
-26
@@ -167,8 +167,8 @@ class Script(object):
|
|||||||
# Allow access on _definition here, because it's a
|
# Allow access on _definition here, because it's a
|
||||||
# public API and we don't want to make the internal
|
# public API and we don't want to make the internal
|
||||||
# Name object public.
|
# Name object public.
|
||||||
if p._name.get_definition().stars == 0: # no *args/**kwargs
|
if p._definition.stars == 0: # no *args/**kwargs
|
||||||
completions.append((p._name.parent, p))
|
completions.append((p._name, p._name))
|
||||||
|
|
||||||
if not path and not isinstance(user_stmt, pr.Import):
|
if not path and not isinstance(user_stmt, pr.Import):
|
||||||
# add keywords
|
# add keywords
|
||||||
@@ -179,17 +179,15 @@ class Script(object):
|
|||||||
comps = []
|
comps = []
|
||||||
comp_dct = {}
|
comp_dct = {}
|
||||||
for c, s in set(completions):
|
for c, s in set(completions):
|
||||||
# TODO Remove this line. c should be a namepart even before that.
|
|
||||||
c = c.names[-1]
|
|
||||||
n = str(c)
|
n = str(c)
|
||||||
if settings.case_insensitive_completion \
|
if settings.case_insensitive_completion \
|
||||||
and n.lower().startswith(like.lower()) \
|
and n.lower().startswith(like.lower()) \
|
||||||
or n.startswith(like):
|
or n.startswith(like):
|
||||||
if not filter_private_variable(s, user_stmt or self._parser.user_scope(), n):
|
if not filter_private_variable(s, user_stmt or self._parser.user_scope(), n):
|
||||||
if isinstance(c.parent.parent, (pr.Function, pr.Class)):
|
if isinstance(c.parent, (pr.Function, pr.Class)):
|
||||||
# TODO I think this is a hack. It should be an
|
# TODO I think this is a hack. It should be an
|
||||||
# er.Function/er.Class before that.
|
# er.Function/er.Class before that.
|
||||||
c = er.wrap(self._evaluator, c.parent.parent).name.names[-1]
|
c = er.wrap(self._evaluator, c.parent).name
|
||||||
new = classes.Completion(self._evaluator, c, needs_dot, len(like), s)
|
new = classes.Completion(self._evaluator, c, needs_dot, len(like), s)
|
||||||
k = (new.name, new.complete) # key
|
k = (new.name, new.complete) # key
|
||||||
if k in comp_dct and settings.no_completion_duplicates:
|
if k in comp_dct and settings.no_completion_duplicates:
|
||||||
@@ -395,10 +393,9 @@ class Script(object):
|
|||||||
definitions = set(self._prepare_goto(goto_path))
|
definitions = set(self._prepare_goto(goto_path))
|
||||||
|
|
||||||
definitions = resolve_import_paths(definitions)
|
definitions = resolve_import_paths(definitions)
|
||||||
names = [s if isinstance(s, pr.Name) else s.name for s in definitions
|
names = [s.name for s in definitions
|
||||||
if s is not imports.ImportWrapper.GlobalNamespace]
|
if s is not imports.ImportWrapper.GlobalNamespace]
|
||||||
defs = [classes.Definition(self._evaluator, name.names[-1])
|
defs = [classes.Definition(self._evaluator, name) for name in names]
|
||||||
for name in names]
|
|
||||||
return helpers.sorted_definitions(set(defs))
|
return helpers.sorted_definitions(set(defs))
|
||||||
|
|
||||||
def goto_assignments(self):
|
def goto_assignments(self):
|
||||||
@@ -432,7 +429,7 @@ class Script(object):
|
|||||||
and d.start_pos == (0, 0):
|
and d.start_pos == (0, 0):
|
||||||
i = imports.ImportWrapper(self._evaluator, d.parent).follow(is_goto=True)
|
i = imports.ImportWrapper(self._evaluator, d.parent).follow(is_goto=True)
|
||||||
definitions.remove(d)
|
definitions.remove(d)
|
||||||
definitions |= follow_inexistent_imports(i.names[-1])
|
definitions |= follow_inexistent_imports(i)
|
||||||
return definitions
|
return definitions
|
||||||
|
|
||||||
goto_path = self._user_context.get_path_under_cursor()
|
goto_path = self._user_context.get_path_under_cursor()
|
||||||
@@ -455,7 +452,7 @@ class Script(object):
|
|||||||
if next(context) in ('class', 'def'):
|
if next(context) in ('class', 'def'):
|
||||||
# The cursor is on a class/function name.
|
# The cursor is on a class/function name.
|
||||||
user_scope = self._parser.user_scope()
|
user_scope = self._parser.user_scope()
|
||||||
definitions = set([user_scope.name.names[-1]])
|
definitions = set([user_scope.name])
|
||||||
elif isinstance(user_stmt, pr.Import):
|
elif isinstance(user_stmt, pr.Import):
|
||||||
s, name_part = helpers.get_on_import_stmt(self._evaluator,
|
s, name_part = helpers.get_on_import_stmt(self._evaluator,
|
||||||
self._user_context, user_stmt)
|
self._user_context, user_stmt)
|
||||||
@@ -467,7 +464,7 @@ class Script(object):
|
|||||||
if add_import_name:
|
if add_import_name:
|
||||||
import_name = user_stmt.get_defined_names()
|
import_name = user_stmt.get_defined_names()
|
||||||
# imports have only one name
|
# imports have only one name
|
||||||
np = import_name[0].names[-1]
|
np = import_name[0]
|
||||||
if not user_stmt.star and unicode(name_part) == unicode(np):
|
if not user_stmt.star and unicode(name_part) == unicode(np):
|
||||||
definitions.append(np)
|
definitions.append(np)
|
||||||
else:
|
else:
|
||||||
@@ -477,8 +474,9 @@ class Script(object):
|
|||||||
if isinstance(user_stmt, pr.ExprStmt):
|
if isinstance(user_stmt, pr.ExprStmt):
|
||||||
for name in user_stmt.get_defined_names():
|
for name in user_stmt.get_defined_names():
|
||||||
if name.start_pos <= self._pos <= name.end_pos \
|
if name.start_pos <= self._pos <= name.end_pos \
|
||||||
and len(name.names) == 1:
|
and (not isinstance(name.parent, pr.Call)
|
||||||
return [name.names[0]]
|
or name.parent.next is None):
|
||||||
|
return [name]
|
||||||
|
|
||||||
defs = self._evaluator.goto(stmt, call_path)
|
defs = self._evaluator.goto(stmt, call_path)
|
||||||
definitions = follow_inexistent_imports(defs)
|
definitions = follow_inexistent_imports(defs)
|
||||||
@@ -504,16 +502,6 @@ class Script(object):
|
|||||||
# Without a definition for a name we cannot find references.
|
# Without a definition for a name we cannot find references.
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Once Script._goto works correct, we can probably remove this
|
|
||||||
# branch.
|
|
||||||
if isinstance(user_stmt, pr.ExprStmt):
|
|
||||||
c = user_stmt.expression_list()[0]
|
|
||||||
if not isinstance(c, unicode) and self._pos < c.start_pos:
|
|
||||||
# The lookup might be before `=`
|
|
||||||
definitions = [v.names[-1] for v in user_stmt.get_defined_names()
|
|
||||||
if unicode(v.names[-1]) ==
|
|
||||||
list(definitions)[0].get_code()]
|
|
||||||
|
|
||||||
if not isinstance(user_stmt, pr.Import):
|
if not isinstance(user_stmt, pr.Import):
|
||||||
# import case is looked at with add_import_name option
|
# import case is looked at with add_import_name option
|
||||||
definitions = usages.usages_add_import_modules(self._evaluator,
|
definitions = usages.usages_add_import_modules(self._evaluator,
|
||||||
@@ -572,7 +560,7 @@ class Script(object):
|
|||||||
key_name = unicode(detail[0][0].name)
|
key_name = unicode(detail[0][0].name)
|
||||||
except (IndexError, AttributeError):
|
except (IndexError, AttributeError):
|
||||||
pass
|
pass
|
||||||
return [classes.CallSignature(self._evaluator, o.name.names[-1], call, index, key_name)
|
return [classes.CallSignature(self._evaluator, o.name, call, index, key_name)
|
||||||
for o in origins if hasattr(o, 'py__call__')]
|
for o in origins if hasattr(o, 'py__call__')]
|
||||||
|
|
||||||
def _analysis(self):
|
def _analysis(self):
|
||||||
@@ -583,7 +571,7 @@ class Script(object):
|
|||||||
iw = imports.ImportWrapper(self._evaluator, i,
|
iw = imports.ImportWrapper(self._evaluator, i,
|
||||||
nested_resolve=True).follow()
|
nested_resolve=True).follow()
|
||||||
if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw):
|
if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw):
|
||||||
analysis.add(self._evaluator, 'import-error', i.namespace.names[-1])
|
analysis.add(self._evaluator, 'import-error', i.namespace_names[-1])
|
||||||
for stmt in sorted(stmts, key=lambda obj: obj.start_pos):
|
for stmt in sorted(stmts, key=lambda obj: obj.start_pos):
|
||||||
if not (isinstance(stmt.parent, pr.ForFlow)
|
if not (isinstance(stmt.parent, pr.ForFlow)
|
||||||
and stmt.parent.set_stmt == stmt):
|
and stmt.parent.set_stmt == stmt):
|
||||||
|
|||||||
+6
-12
@@ -36,8 +36,7 @@ def defined_names(evaluator, scope):
|
|||||||
pair = next(get_names_of_scope(evaluator, scope, star_search=False,
|
pair = next(get_names_of_scope(evaluator, scope, star_search=False,
|
||||||
include_builtin=False), None)
|
include_builtin=False), None)
|
||||||
names = pair[1] if pair else []
|
names = pair[1] if pair else []
|
||||||
names = [n for n in names if isinstance(n, pr.Import) or (len(n) == 1)]
|
return [Definition(evaluator, d) for d in sorted(names, key=lambda s: s.start_pos)]
|
||||||
return [Definition(evaluator, d.names[-1]) for d in sorted(names, key=lambda s: s.start_pos)]
|
|
||||||
|
|
||||||
|
|
||||||
class BaseDefinition(object):
|
class BaseDefinition(object):
|
||||||
@@ -168,16 +167,11 @@ class BaseDefinition(object):
|
|||||||
def _path(self):
|
def _path(self):
|
||||||
"""The module path."""
|
"""The module path."""
|
||||||
path = []
|
path = []
|
||||||
|
|
||||||
def insert_nonnone(x):
|
|
||||||
if x:
|
|
||||||
path.insert(0, x)
|
|
||||||
|
|
||||||
par = self._definition
|
par = self._definition
|
||||||
while par is not None:
|
while par is not None:
|
||||||
if isinstance(par, pr.Import):
|
if isinstance(par, pr.Import):
|
||||||
insert_nonnone(par.namespace)
|
path += par.from_names
|
||||||
insert_nonnone(par.from_ns)
|
path += par.namespace_names
|
||||||
if par.relative_count == 0:
|
if par.relative_count == 0:
|
||||||
break
|
break
|
||||||
with common.ignored(AttributeError):
|
with common.ignored(AttributeError):
|
||||||
@@ -376,12 +370,12 @@ class BaseDefinition(object):
|
|||||||
params = sub.params[1:] # ignore self
|
params = sub.params[1:] # ignore self
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return []
|
return []
|
||||||
return [_Param(self._evaluator, p.get_name().names[-1]) for p in params]
|
return [_Param(self._evaluator, p.get_name()) for p in params]
|
||||||
|
|
||||||
def parent(self):
|
def parent(self):
|
||||||
scope = self._definition.get_parent_scope()
|
scope = self._definition.get_parent_scope()
|
||||||
non_flow = scope.get_parent_until(pr.Flow, reverse=True)
|
non_flow = scope.get_parent_until(pr.Flow, reverse=True)
|
||||||
return Definition(self._evaluator, non_flow.name.names[-1])
|
return Definition(self._evaluator, non_flow.name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s %s>" % (type(self).__name__, self.description)
|
return "<%s %s>" % (type(self).__name__, self.description)
|
||||||
@@ -539,7 +533,7 @@ class Completion(BaseDefinition):
|
|||||||
it's just PITA-slow.
|
it's just PITA-slow.
|
||||||
"""
|
"""
|
||||||
defs = self._follow_statements_imports()
|
defs = self._follow_statements_imports()
|
||||||
return [Definition(self._evaluator, d.name.names[-1]) for d in defs]
|
return [Definition(self._evaluator, d.name) for d in defs]
|
||||||
|
|
||||||
|
|
||||||
class Definition(use_metaclass(CachedMetaClass, BaseDefinition)):
|
class Definition(use_metaclass(CachedMetaClass, BaseDefinition)):
|
||||||
|
|||||||
+7
-7
@@ -28,14 +28,14 @@ def get_on_import_stmt(evaluator, user_context, user_stmt, is_like_search=False)
|
|||||||
import_names = user_stmt.get_all_import_names()
|
import_names = user_stmt.get_all_import_names()
|
||||||
kill_count = -1
|
kill_count = -1
|
||||||
cur_name_part = None
|
cur_name_part = None
|
||||||
for i in import_names:
|
for name in import_names:
|
||||||
if user_stmt.alias == i:
|
if user_stmt.alias == name:
|
||||||
continue
|
continue
|
||||||
for name_part in i.names:
|
|
||||||
if name_part.end_pos >= user_context.position:
|
if name.end_pos >= user_context.position:
|
||||||
if not cur_name_part:
|
if not cur_name_part:
|
||||||
cur_name_part = name_part
|
cur_name_part = name
|
||||||
kill_count += 1
|
kill_count += 1
|
||||||
|
|
||||||
context = user_context.get_context()
|
context = user_context.get_context()
|
||||||
just_from = next(context) == 'from'
|
just_from = next(context) == 'from'
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ class LazyName(helpers.FakeName):
|
|||||||
module = obj
|
module = obj
|
||||||
else:
|
else:
|
||||||
class FakeParent(pr.Base):
|
class FakeParent(pr.Base):
|
||||||
parent = None # To avoid having no parent for NamePart.
|
parent = compiled.builtin
|
||||||
path = None
|
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
try:
|
try:
|
||||||
@@ -67,8 +66,7 @@ class LazyName(helpers.FakeName):
|
|||||||
module = builtins
|
module = builtins
|
||||||
else:
|
else:
|
||||||
module = __import__(module_name)
|
module = __import__(module_name)
|
||||||
fake_name = helpers.FakeName(names, FakeParent())
|
parser_path = [helpers.FakeName(n, FakeParent()) for n in names]
|
||||||
parser_path = fake_name.names
|
|
||||||
raw_module = get_module(self._value)
|
raw_module = get_module(self._value)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import pydoc
|
import pydoc
|
||||||
import keyword
|
import keyword
|
||||||
|
|
||||||
from jedi.parser.representation import NamePart
|
|
||||||
from jedi._compatibility import is_py3
|
from jedi._compatibility import is_py3
|
||||||
from jedi import common
|
from jedi import common
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
|
|||||||
+7
-9
@@ -8,7 +8,7 @@ from jedi.evaluate import helpers
|
|||||||
|
|
||||||
def usages(evaluator, definitions, mods):
|
def usages(evaluator, definitions, mods):
|
||||||
"""
|
"""
|
||||||
:param definitions: list of NameParts
|
:param definitions: list of Name
|
||||||
"""
|
"""
|
||||||
def compare_array(definitions):
|
def compare_array(definitions):
|
||||||
""" `definitions` are being compared by module/start_pos, because
|
""" `definitions` are being compared by module/start_pos, because
|
||||||
@@ -25,8 +25,7 @@ def usages(evaluator, definitions, mods):
|
|||||||
while not stmt.parent.is_scope():
|
while not stmt.parent.is_scope():
|
||||||
stmt = stmt.parent
|
stmt = stmt.parent
|
||||||
# New definition, call cannot be a part of stmt
|
# New definition, call cannot be a part of stmt
|
||||||
if len(call.name) == 1 and call.next is None \
|
if call.next is None and call.name in stmt.get_defined_names():
|
||||||
and call.name in stmt.get_defined_names():
|
|
||||||
# Class params are not definitions (like function params). They
|
# Class params are not definitions (like function params). They
|
||||||
# are super classes, that need to be resolved.
|
# are super classes, that need to be resolved.
|
||||||
if not (isinstance(stmt, pr.Param) and isinstance(stmt.parent, pr.Class)):
|
if not (isinstance(stmt, pr.Param) and isinstance(stmt.parent, pr.Class)):
|
||||||
@@ -35,7 +34,7 @@ def usages(evaluator, definitions, mods):
|
|||||||
follow = [] # There might be multiple search_name's in one call_path
|
follow = [] # There might be multiple search_name's in one call_path
|
||||||
call_path = list(call.generate_call_path())
|
call_path = list(call.generate_call_path())
|
||||||
for i, name in enumerate(call_path):
|
for i, name in enumerate(call_path):
|
||||||
# name is `pr.NamePart`.
|
# name is `pr.Name`.
|
||||||
if u(name) == search_name:
|
if u(name) == search_name:
|
||||||
follow.append(call_path[:i + 1])
|
follow.append(call_path[:i + 1])
|
||||||
|
|
||||||
@@ -70,11 +69,10 @@ def usages(evaluator, definitions, mods):
|
|||||||
if isinstance(stmt, pr.Import):
|
if isinstance(stmt, pr.Import):
|
||||||
count = 0
|
count = 0
|
||||||
imps = []
|
imps = []
|
||||||
for i in stmt.get_all_import_names():
|
for name in stmt.get_all_import_names():
|
||||||
for name_part in i.names:
|
count += 1
|
||||||
count += 1
|
if unicode(name) == search_name:
|
||||||
if unicode(name_part) == search_name:
|
imps.append((count, name))
|
||||||
imps.append((count, name_part))
|
|
||||||
|
|
||||||
for used_count, name_part in imps:
|
for used_count, name_part in imps:
|
||||||
i = imports.ImportWrapper(evaluator, stmt, kill_count=count - used_count,
|
i = imports.ImportWrapper(evaluator, stmt, kill_count=count - used_count,
|
||||||
|
|||||||
+1
-1
@@ -243,7 +243,7 @@ def save_parser(path, name, parser, pickling=True):
|
|||||||
|
|
||||||
class ParserPickling(object):
|
class ParserPickling(object):
|
||||||
|
|
||||||
version = 17
|
version = 18
|
||||||
"""
|
"""
|
||||||
Version number (integer) for file system cache.
|
Version number (integer) for file system cache.
|
||||||
|
|
||||||
|
|||||||
+41
-23
@@ -69,7 +69,7 @@ backtracking algorithm.
|
|||||||
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
|
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
from itertools import tee, chain
|
||||||
|
|
||||||
from jedi._compatibility import next, hasattr, unicode
|
from jedi._compatibility import next, hasattr, unicode
|
||||||
from jedi.parser import representation as pr
|
from jedi.parser import representation as pr
|
||||||
@@ -172,20 +172,7 @@ class Evaluator(object):
|
|||||||
return precedence.process_precedence_element(self, p) or []
|
return precedence.process_precedence_element(self, p) or []
|
||||||
|
|
||||||
def eval_statement_element(self, element):
|
def eval_statement_element(self, element):
|
||||||
if pr.Array.is_type(element, pr.Array.NOARRAY):
|
if isinstance(element, pr.ListComprehension):
|
||||||
try:
|
|
||||||
lst_cmp = element[0].expression_list()[0]
|
|
||||||
if not isinstance(lst_cmp, pr.ListComprehension):
|
|
||||||
raise IndexError
|
|
||||||
except IndexError:
|
|
||||||
r = list(itertools.chain.from_iterable(self.eval_statement(s)
|
|
||||||
for s in element))
|
|
||||||
else:
|
|
||||||
r = [iterable.GeneratorComprehension(self, lst_cmp)]
|
|
||||||
call_path = element.generate_call_path()
|
|
||||||
next(call_path, None) # the first one has been used already
|
|
||||||
return self.follow_path(call_path, r, element.parent)
|
|
||||||
elif isinstance(element, pr.ListComprehension):
|
|
||||||
return self.eval_statement(element.stmt)
|
return self.eval_statement(element.stmt)
|
||||||
elif isinstance(element, pr.Lambda):
|
elif isinstance(element, pr.Lambda):
|
||||||
return [er.Function(self, element)]
|
return [er.Function(self, element)]
|
||||||
@@ -219,9 +206,20 @@ class Evaluator(object):
|
|||||||
current = next(path)
|
current = next(path)
|
||||||
|
|
||||||
if isinstance(current, pr.Array):
|
if isinstance(current, pr.Array):
|
||||||
types = [iterable.Array(self, current)]
|
if current.type == pr.Array.NOARRAY:
|
||||||
|
try:
|
||||||
|
lst_cmp = current[0].expression_list()[0]
|
||||||
|
if not isinstance(lst_cmp, pr.ListComprehension):
|
||||||
|
raise IndexError
|
||||||
|
except IndexError:
|
||||||
|
types = list(chain.from_iterable(self.eval_statement(s)
|
||||||
|
for s in current))
|
||||||
|
else:
|
||||||
|
types = [iterable.GeneratorComprehension(self, lst_cmp)]
|
||||||
|
else:
|
||||||
|
types = [iterable.Array(self, current)]
|
||||||
else:
|
else:
|
||||||
if isinstance(current, pr.NamePart):
|
if isinstance(current, pr.Name):
|
||||||
# This is the first global lookup.
|
# This is the first global lookup.
|
||||||
types = self.find_types(scope, current, position=position,
|
types = self.find_types(scope, current, position=position,
|
||||||
search_global=True)
|
search_global=True)
|
||||||
@@ -241,7 +239,7 @@ class Evaluator(object):
|
|||||||
to follow a call like ``module.a_type.Foo.bar`` (in ``from_somewhere``).
|
to follow a call like ``module.a_type.Foo.bar`` (in ``from_somewhere``).
|
||||||
"""
|
"""
|
||||||
results_new = []
|
results_new = []
|
||||||
iter_paths = itertools.tee(path, len(types))
|
iter_paths = tee(path, len(types))
|
||||||
|
|
||||||
for i, typ in enumerate(types):
|
for i, typ in enumerate(types):
|
||||||
fp = self._follow_path(iter_paths[i], typ, call_scope)
|
fp = self._follow_path(iter_paths[i], typ, call_scope)
|
||||||
@@ -320,12 +318,26 @@ class Evaluator(object):
|
|||||||
return types
|
return types
|
||||||
|
|
||||||
def goto(self, stmt, call_path):
|
def goto(self, stmt, call_path):
|
||||||
|
if isinstance(stmt, pr.Import):
|
||||||
|
# Nowhere to goto for aliases
|
||||||
|
if stmt.alias == call_path[0]:
|
||||||
|
return [call_path[0]]
|
||||||
|
|
||||||
|
names = stmt.get_all_import_names()
|
||||||
|
if stmt.alias:
|
||||||
|
names = names[:-1]
|
||||||
|
# Filter names that are after our Name
|
||||||
|
removed_names = len(names) - names.index(call_path[0]) - 1
|
||||||
|
i = imports.ImportWrapper(self, stmt, kill_count=removed_names,
|
||||||
|
nested_resolve=True)
|
||||||
|
return i.follow(is_goto=True)
|
||||||
|
|
||||||
# Return the name defined in the call_path, if it's part of the
|
# Return the name defined in the call_path, if it's part of the
|
||||||
# statement name definitions. Only return, if it's one name and one
|
# statement name definitions. Only return, if it's one name and one
|
||||||
# name only. Otherwise it's a mixture between a definition and a
|
# name only. Otherwise it's a mixture between a definition and a
|
||||||
# reference. In this case it's just a definition. So we stay on it.
|
# reference. In this case it's just a definition. So we stay on it.
|
||||||
if len(call_path) == 1 and isinstance(call_path[0], pr.NamePart) \
|
if len(call_path) == 1 and isinstance(call_path[0], pr.Name) \
|
||||||
and call_path[0] in [d.names[-1] for d in stmt.get_defined_names()]:
|
and call_path[0] in stmt.get_defined_names():
|
||||||
# Named params should get resolved to their param definitions.
|
# Named params should get resolved to their param definitions.
|
||||||
if pr.Array.is_type(stmt.parent, pr.Array.TUPLE, pr.Array.NOARRAY) \
|
if pr.Array.is_type(stmt.parent, pr.Array.TUPLE, pr.Array.NOARRAY) \
|
||||||
and stmt.parent.previous:
|
and stmt.parent.previous:
|
||||||
@@ -337,9 +349,15 @@ class Evaluator(object):
|
|||||||
param_names = []
|
param_names = []
|
||||||
named_param_name = stmt.get_defined_names()[0]
|
named_param_name = stmt.get_defined_names()[0]
|
||||||
for typ in self.eval_call(call):
|
for typ in self.eval_call(call):
|
||||||
for param in typ.params:
|
if isinstance(typ, er.Class):
|
||||||
|
params = []
|
||||||
|
for init_method in typ.py__getattribute__('__init__'):
|
||||||
|
params += init_method.params
|
||||||
|
else:
|
||||||
|
params = typ.params
|
||||||
|
for param in params:
|
||||||
if unicode(param.get_name()) == unicode(named_param_name):
|
if unicode(param.get_name()) == unicode(named_param_name):
|
||||||
param_names.append(param.get_name().names[-1])
|
param_names.append(param.get_name())
|
||||||
return param_names
|
return param_names
|
||||||
return [call_path[0]]
|
return [call_path[0]]
|
||||||
|
|
||||||
@@ -364,7 +382,7 @@ class Evaluator(object):
|
|||||||
|
|
||||||
def filter_private_variable(scope, call_scope, var_name):
|
def filter_private_variable(scope, call_scope, var_name):
|
||||||
"""private variables begin with a double underline `__`"""
|
"""private variables begin with a double underline `__`"""
|
||||||
var_name = str(var_name) # var_name could be a NamePart
|
var_name = str(var_name) # var_name could be a Name
|
||||||
if isinstance(var_name, (str, unicode)) and isinstance(scope, er.Instance)\
|
if isinstance(var_name, (str, unicode)) and isinstance(scope, er.Instance)\
|
||||||
and var_name.startswith('__') and not var_name.endswith('__'):
|
and var_name.startswith('__') and not var_name.endswith('__'):
|
||||||
s = call_scope.get_parent_until((pr.Class, er.Instance, compiled.CompiledObject))
|
s = call_scope.get_parent_until((pr.Class, er.Instance, compiled.CompiledObject))
|
||||||
|
|||||||
@@ -240,7 +240,6 @@ class CompiledName(FakeName):
|
|||||||
super(CompiledName, self).__init__(name)
|
super(CompiledName, self).__init__(name)
|
||||||
self._obj = obj
|
self._obj = obj
|
||||||
self.name = name
|
self.name = name
|
||||||
self.start_pos = 0, 0 # an illegal start_pos, to make sorting easy.
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ def search_params(evaluator, param):
|
|||||||
|
|
||||||
# Need to take right index, because there could be a
|
# Need to take right index, because there could be a
|
||||||
# func usage before.
|
# func usage before.
|
||||||
call_path_simple = [unicode(d) if isinstance(d, pr.NamePart)
|
call_path_simple = [unicode(d) if isinstance(d, pr.Name)
|
||||||
else d for d in call_path]
|
else d for d in call_path]
|
||||||
i = listRightIndex(call_path_simple, func_name)
|
i = listRightIndex(call_path_simple, func_name)
|
||||||
before, after = call_path[:i], call_path[i + 1:]
|
before, after = call_path[:i], call_path[i + 1:]
|
||||||
@@ -121,7 +121,7 @@ def search_params(evaluator, param):
|
|||||||
for params in get_posibilities(evaluator, module, func_name):
|
for params in get_posibilities(evaluator, module, func_name):
|
||||||
for p in params:
|
for p in params:
|
||||||
if str(p) == param_name:
|
if str(p) == param_name:
|
||||||
result += evaluator.eval_statement(p.parent)
|
result += evaluator.eval_statement(p.get_definition())
|
||||||
return result
|
return result
|
||||||
|
|
||||||
func = param.get_parent_until(pr.Function)
|
func = param.get_parent_until(pr.Function)
|
||||||
|
|||||||
+19
-15
@@ -42,7 +42,7 @@ class NameFinder(object):
|
|||||||
types = self._names_to_types(names, resolve_decorator)
|
types = self._names_to_types(names, resolve_decorator)
|
||||||
|
|
||||||
if not names and not types \
|
if not names and not types \
|
||||||
and not (isinstance(self.name_str, pr.NamePart)
|
and not (isinstance(self.name_str, pr.Name)
|
||||||
and isinstance(self.name_str.parent.parent, pr.Param)):
|
and isinstance(self.name_str.parent.parent, pr.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 search_global:
|
||||||
@@ -102,18 +102,18 @@ class NameFinder(object):
|
|||||||
or isinstance(scope, compiled.CompiledObject) \
|
or isinstance(scope, compiled.CompiledObject) \
|
||||||
or isinstance(stmt, pr.ExprStmt) and stmt.is_global():
|
or isinstance(stmt, pr.ExprStmt) and stmt.is_global():
|
||||||
# Always reachable.
|
# Always reachable.
|
||||||
names.append(name.names[-1])
|
names.append(name)
|
||||||
else:
|
else:
|
||||||
check = flow_analysis.break_check(self._evaluator,
|
check = flow_analysis.break_check(self._evaluator,
|
||||||
name_list_scope,
|
name_list_scope,
|
||||||
er.wrap(self._evaluator, scope),
|
er.wrap(self._evaluator, scope),
|
||||||
self.scope)
|
self.scope)
|
||||||
if check is not flow_analysis.UNREACHABLE:
|
if check is not flow_analysis.UNREACHABLE:
|
||||||
names.append(name.names[-1])
|
names.append(name)
|
||||||
if check is flow_analysis.REACHABLE:
|
if check is flow_analysis.REACHABLE:
|
||||||
break
|
break
|
||||||
|
|
||||||
if names and self._is_name_break_scope(name, stmt):
|
if names and self._is_name_break_scope(stmt):
|
||||||
if self._does_scope_break_immediately(scope, name_list_scope):
|
if self._does_scope_break_immediately(scope, name_list_scope):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@@ -139,16 +139,16 @@ class NameFinder(object):
|
|||||||
evaluation, so remove them already here!
|
evaluation, so remove them already here!
|
||||||
"""
|
"""
|
||||||
for n in names:
|
for n in names:
|
||||||
definition = n.parent.parent
|
definition = n.parent
|
||||||
if isinstance(definition, (pr.Function, pr.Class, pr.Module)):
|
if isinstance(definition, (pr.Function, pr.Class, pr.Module)):
|
||||||
yield er.wrap(self._evaluator, definition).name.names[-1]
|
yield er.wrap(self._evaluator, definition).name
|
||||||
else:
|
else:
|
||||||
yield n
|
yield n
|
||||||
|
|
||||||
def _check_getattr(self, inst):
|
def _check_getattr(self, inst):
|
||||||
"""Checks for both __getattr__ and __getattribute__ methods"""
|
"""Checks for both __getattr__ and __getattribute__ methods"""
|
||||||
result = []
|
result = []
|
||||||
# str is important to lose the NamePart!
|
# str is important, because it shouldn't be `Name`!
|
||||||
name = compiled.create(self._evaluator, str(self.name_str))
|
name = compiled.create(self._evaluator, str(self.name_str))
|
||||||
with common.ignored(KeyError):
|
with common.ignored(KeyError):
|
||||||
result = inst.execute_subscope_by_name('__getattr__', [name])
|
result = inst.execute_subscope_by_name('__getattr__', [name])
|
||||||
@@ -161,12 +161,12 @@ class NameFinder(object):
|
|||||||
result = inst.execute_subscope_by_name('__getattribute__', [name])
|
result = inst.execute_subscope_by_name('__getattribute__', [name])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _is_name_break_scope(self, name, stmt):
|
def _is_name_break_scope(self, stmt):
|
||||||
"""
|
"""
|
||||||
Returns True except for nested imports and instance variables.
|
Returns True except for nested imports and instance variables.
|
||||||
"""
|
"""
|
||||||
if stmt.isinstance(pr.ExprStmt):
|
if stmt.isinstance(pr.ExprStmt):
|
||||||
if isinstance(name, er.InstanceElement) and not name.is_class_var:
|
if isinstance(stmt, er.InstanceElement) and not stmt.is_class_var:
|
||||||
return False
|
return False
|
||||||
elif isinstance(stmt, pr.Import) and stmt.is_nested():
|
elif isinstance(stmt, pr.Import) and stmt.is_nested():
|
||||||
return False
|
return False
|
||||||
@@ -219,7 +219,7 @@ class NameFinder(object):
|
|||||||
evaluator = self._evaluator
|
evaluator = self._evaluator
|
||||||
|
|
||||||
# Add isinstance and other if/assert knowledge.
|
# Add isinstance and other if/assert knowledge.
|
||||||
if isinstance(self.name_str, pr.NamePart):
|
if isinstance(self.name_str, pr.Name):
|
||||||
flow_scope = self.name_str.parent.parent
|
flow_scope = self.name_str.parent.parent
|
||||||
# Ignore FunctionExecution parents for now.
|
# Ignore FunctionExecution parents for now.
|
||||||
until = flow_scope.get_parent_until(er.FunctionExecution)
|
until = flow_scope.get_parent_until(er.FunctionExecution)
|
||||||
@@ -281,7 +281,7 @@ class NameFinder(object):
|
|||||||
if isinstance(p, pr.Flow) and p.command == 'except' and p.inputs:
|
if isinstance(p, pr.Flow) and p.command == 'except' and p.inputs:
|
||||||
as_names = p.inputs[0].as_names
|
as_names = p.inputs[0].as_names
|
||||||
try:
|
try:
|
||||||
if as_names[0].names[-1] == name:
|
if as_names[0] == name:
|
||||||
# TODO check for types that are not classes and add it to
|
# TODO check for types that are not classes and add it to
|
||||||
# the static analysis report.
|
# the static analysis report.
|
||||||
types = list(chain.from_iterable(
|
types = list(chain.from_iterable(
|
||||||
@@ -395,7 +395,7 @@ def check_flow_information(evaluator, flow, search_name_part, pos):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _check_isinstance_type(evaluator, stmt, search_name_part):
|
def _check_isinstance_type(evaluator, stmt, search_name):
|
||||||
try:
|
try:
|
||||||
expression_list = stmt.expression_list()
|
expression_list = stmt.expression_list()
|
||||||
# this might be removed if we analyze and, etc
|
# this might be removed if we analyze and, etc
|
||||||
@@ -412,8 +412,12 @@ def _check_isinstance_type(evaluator, stmt, search_name_part):
|
|||||||
assert len(classes) == 1
|
assert len(classes) == 1
|
||||||
assert isinstance(obj[0], pr.Call)
|
assert isinstance(obj[0], pr.Call)
|
||||||
|
|
||||||
# names fit?
|
prev = search_name.parent
|
||||||
assert unicode(obj[0].name) == unicode(search_name_part.parent)
|
while prev.previous is not None:
|
||||||
|
prev = prev.previous
|
||||||
|
# Do a simple get_code comparison. They should just have the same code,
|
||||||
|
# and everything will be all right.
|
||||||
|
assert obj[0].get_code() == prev.get_code()
|
||||||
assert isinstance(classes[0], pr.StatementElement) # can be type or tuple
|
assert isinstance(classes[0], pr.StatementElement) # can be type or tuple
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
return []
|
return []
|
||||||
@@ -583,7 +587,7 @@ def find_assignments(lhs, results, seek_name):
|
|||||||
"""
|
"""
|
||||||
if isinstance(lhs, pr.Array):
|
if isinstance(lhs, pr.Array):
|
||||||
return _assign_tuples(lhs, results, seek_name)
|
return _assign_tuples(lhs, results, seek_name)
|
||||||
elif unicode(lhs.name.names[-1]) == seek_name:
|
elif unicode(lhs.name) == seek_name:
|
||||||
return results
|
return results
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|||||||
+18
-17
@@ -1,6 +1,7 @@
|
|||||||
import copy
|
import copy
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
from jedi._compatibility import unicode
|
||||||
from jedi.parser import representation as pr
|
from jedi.parser import representation as pr
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ def deep_ast_copy(obj, new_elements_default=None):
|
|||||||
return key_value[0] not in ('_expression_list', '_assignment_details')
|
return key_value[0] not in ('_expression_list', '_assignment_details')
|
||||||
|
|
||||||
new_elements = new_elements_default or {}
|
new_elements = new_elements_default or {}
|
||||||
accept = (pr.Simple, pr.NamePart, pr.KeywordStatement)
|
accept = (pr.Simple, pr.Name, pr.KeywordStatement)
|
||||||
|
|
||||||
def recursion(obj):
|
def recursion(obj):
|
||||||
# If it's already in the cache, just return it.
|
# If it's already in the cache, just return it.
|
||||||
@@ -50,6 +51,8 @@ def deep_ast_copy(obj, new_elements_default=None):
|
|||||||
# because there are several references that don't walk the whole
|
# because there are several references that don't walk the whole
|
||||||
# tree in there.
|
# tree in there.
|
||||||
items = sorted(items, key=sort_stmt)
|
items = sorted(items, key=sort_stmt)
|
||||||
|
else:
|
||||||
|
items = sorted(items, key=lambda x: x[0] == '_names_dict')
|
||||||
|
|
||||||
# Actually copy and set attributes.
|
# Actually copy and set attributes.
|
||||||
new_obj = copy.copy(obj)
|
new_obj = copy.copy(obj)
|
||||||
@@ -67,13 +70,17 @@ def deep_ast_copy(obj, new_elements_default=None):
|
|||||||
pass
|
pass
|
||||||
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
|
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
|
||||||
continue
|
continue
|
||||||
|
elif key == '_names_dict':
|
||||||
|
d = dict((k, sequence_recursion(v)) for k, v in value.items())
|
||||||
|
setattr(new_obj, key, d)
|
||||||
elif isinstance(value, (list, tuple)):
|
elif isinstance(value, (list, tuple)):
|
||||||
setattr(new_obj, key, list_or_tuple_rec(value))
|
setattr(new_obj, key, sequence_recursion(value))
|
||||||
elif isinstance(value, accept):
|
elif isinstance(value, accept):
|
||||||
setattr(new_obj, key, recursion(value))
|
setattr(new_obj, key, recursion(value))
|
||||||
|
|
||||||
return new_obj
|
return new_obj
|
||||||
|
|
||||||
def list_or_tuple_rec(array_obj):
|
def sequence_recursion(array_obj):
|
||||||
if isinstance(array_obj, tuple):
|
if isinstance(array_obj, tuple):
|
||||||
copied_array = list(array_obj)
|
copied_array = list(array_obj)
|
||||||
else:
|
else:
|
||||||
@@ -82,7 +89,7 @@ def deep_ast_copy(obj, new_elements_default=None):
|
|||||||
if isinstance(el, accept):
|
if isinstance(el, accept):
|
||||||
copied_array[i] = recursion(el)
|
copied_array[i] = recursion(el)
|
||||||
elif isinstance(el, (tuple, list)):
|
elif isinstance(el, (tuple, list)):
|
||||||
copied_array[i] = list_or_tuple_rec(el)
|
copied_array[i] = sequence_recursion(el)
|
||||||
|
|
||||||
if isinstance(array_obj, tuple):
|
if isinstance(array_obj, tuple):
|
||||||
return tuple(copied_array)
|
return tuple(copied_array)
|
||||||
@@ -196,9 +203,7 @@ def scan_statement_for_calls(stmt, search_name, assignment_details=False):
|
|||||||
if isinstance(s_new, pr.Array):
|
if isinstance(s_new, pr.Array):
|
||||||
result += scan_array(s_new, search_name)
|
result += scan_array(s_new, search_name)
|
||||||
else:
|
else:
|
||||||
n = s_new.name
|
if search_name == unicode(s_new.name):
|
||||||
if isinstance(n, pr.Name) \
|
|
||||||
and search_name in [str(x) for x in n.names]:
|
|
||||||
result.append(c)
|
result.append(c)
|
||||||
|
|
||||||
s_new = s_new.next
|
s_new = s_new.next
|
||||||
@@ -217,7 +222,7 @@ def get_module_name_parts(module):
|
|||||||
def scope_name_parts(scope):
|
def scope_name_parts(scope):
|
||||||
for s in scope.subscopes:
|
for s in scope.subscopes:
|
||||||
# Yield the name parts, not names.
|
# Yield the name parts, not names.
|
||||||
yield s.name.names[0]
|
yield s.name
|
||||||
for need_yield_from in scope_name_parts(s):
|
for need_yield_from in scope_name_parts(s):
|
||||||
yield need_yield_from
|
yield need_yield_from
|
||||||
|
|
||||||
@@ -226,7 +231,7 @@ def get_module_name_parts(module):
|
|||||||
for stmt_or_import in statements_or_imports:
|
for stmt_or_import in statements_or_imports:
|
||||||
if isinstance(stmt_or_import, pr.Import):
|
if isinstance(stmt_or_import, pr.Import):
|
||||||
for name in stmt_or_import.get_all_import_names():
|
for name in stmt_or_import.get_all_import_names():
|
||||||
name_parts.update(name.names)
|
name_parts.add(name)
|
||||||
else:
|
else:
|
||||||
# Running this ensures that all the expression lists are generated
|
# Running this ensures that all the expression lists are generated
|
||||||
# and the parents are all set. (Important for Lambdas) Howeer, this
|
# and the parents are all set. (Important for Lambdas) Howeer, this
|
||||||
@@ -238,7 +243,7 @@ def get_module_name_parts(module):
|
|||||||
# all the name_parts.
|
# all the name_parts.
|
||||||
for tok in stmt_or_import._token_list:
|
for tok in stmt_or_import._token_list:
|
||||||
if isinstance(tok, pr.Name):
|
if isinstance(tok, pr.Name):
|
||||||
name_parts.update(tok.names)
|
name_parts.add(tok)
|
||||||
|
|
||||||
return name_parts
|
return name_parts
|
||||||
|
|
||||||
@@ -298,18 +303,14 @@ class FakeStatement(pr.ExprStmt):
|
|||||||
class FakeImport(pr.Import):
|
class FakeImport(pr.Import):
|
||||||
def __init__(self, name, parent, level=0):
|
def __init__(self, name, parent, level=0):
|
||||||
p = 0, 0
|
p = 0, 0
|
||||||
super(FakeImport, self).__init__(FakeSubModule, p, p, name,
|
super(FakeImport, self).__init__(FakeSubModule, p, p, [name],
|
||||||
relative_count=level)
|
relative_count=level)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
|
|
||||||
class FakeName(pr.Name):
|
class FakeName(pr.Name):
|
||||||
def __init__(self, name_or_names, parent=None, start_pos=(0, 0)):
|
def __init__(self, name_str, parent=None, start_pos=(0, 0)):
|
||||||
if isinstance(name_or_names, list):
|
super(FakeName, self).__init__(FakeSubModule, name_str, parent, start_pos)
|
||||||
names = [(n, start_pos) for n in name_or_names]
|
|
||||||
else:
|
|
||||||
names = [(name_or_names, start_pos)]
|
|
||||||
super(FakeName, self).__init__(FakeSubModule, names, start_pos, start_pos, parent)
|
|
||||||
|
|
||||||
def get_definition(self):
|
def get_definition(self):
|
||||||
return self.parent
|
return self.parent
|
||||||
|
|||||||
+70
-65
@@ -67,13 +67,13 @@ class ImportWrapper(pr.Base):
|
|||||||
|
|
||||||
# rest is import_path resolution
|
# rest is import_path resolution
|
||||||
import_path = []
|
import_path = []
|
||||||
if import_stmt.from_ns:
|
if import_stmt.from_names:
|
||||||
import_path += import_stmt.from_ns.names
|
import_path += import_stmt.from_names
|
||||||
if import_stmt.namespace:
|
if import_stmt.namespace_names:
|
||||||
if self.import_stmt.is_nested() and not nested_resolve:
|
if self.import_stmt.is_nested() and not nested_resolve:
|
||||||
import_path.append(import_stmt.namespace.names[0])
|
import_path.append(import_stmt.namespace_names[0])
|
||||||
else:
|
else:
|
||||||
import_path += import_stmt.namespace.names
|
import_path += import_stmt.namespace_names
|
||||||
|
|
||||||
for i in range(kill_count + int(is_like_search)):
|
for i in range(kill_count + int(is_like_search)):
|
||||||
if import_path:
|
if import_path:
|
||||||
@@ -110,6 +110,7 @@ class ImportWrapper(pr.Base):
|
|||||||
m = _load_module(rel_path)
|
m = _load_module(rel_path)
|
||||||
names += m.get_defined_names()
|
names += m.get_defined_names()
|
||||||
else:
|
else:
|
||||||
|
# flask
|
||||||
if self.import_path == ('flask', 'ext'):
|
if self.import_path == ('flask', 'ext'):
|
||||||
# List Flask extensions like ``flask_foo``
|
# List Flask extensions like ``flask_foo``
|
||||||
for mod in self._get_module_names():
|
for mod in self._get_module_names():
|
||||||
@@ -122,6 +123,8 @@ class ImportWrapper(pr.Base):
|
|||||||
flaskext = os.path.join(dir, 'flaskext')
|
flaskext = os.path.join(dir, 'flaskext')
|
||||||
if os.path.isdir(flaskext):
|
if os.path.isdir(flaskext):
|
||||||
names += self._get_module_names([flaskext])
|
names += self._get_module_names([flaskext])
|
||||||
|
|
||||||
|
# namespace packages
|
||||||
if on_import_stmt and isinstance(scope, pr.Module) \
|
if on_import_stmt and isinstance(scope, pr.Module) \
|
||||||
and scope.path.endswith('__init__.py'):
|
and scope.path.endswith('__init__.py'):
|
||||||
pkg_path = os.path.dirname(scope.path)
|
pkg_path = os.path.dirname(scope.path)
|
||||||
@@ -136,18 +139,18 @@ class ImportWrapper(pr.Base):
|
|||||||
# ``sys.modules`` modification.
|
# ``sys.modules`` modification.
|
||||||
names.append(self._generate_name('path'))
|
names.append(self._generate_name('path'))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not self.import_stmt.from_names or self.is_partial_import:
|
||||||
|
# from_names must be defined to access module
|
||||||
|
# values plus a partial import means that there
|
||||||
|
# is something after the import, which
|
||||||
|
# automatically implies that there must not be
|
||||||
|
# any non-module scope.
|
||||||
|
continue
|
||||||
from jedi.evaluate import finder
|
from jedi.evaluate import finder
|
||||||
for s, scope_names in finder.get_names_of_scope(self._evaluator,
|
for s, scope_names in finder.get_names_of_scope(self._evaluator,
|
||||||
scope, include_builtin=False):
|
scope, include_builtin=False):
|
||||||
for n in scope_names:
|
for n in scope_names:
|
||||||
if self.import_stmt.from_ns is None \
|
|
||||||
or self.is_partial_import:
|
|
||||||
# from_ns must be defined to access module
|
|
||||||
# values plus a partial import means that there
|
|
||||||
# is something after the import, which
|
|
||||||
# automatically implies that there must not be
|
|
||||||
# any non-module scope.
|
|
||||||
continue
|
|
||||||
names.append(n)
|
names.append(n)
|
||||||
return names
|
return names
|
||||||
|
|
||||||
@@ -179,55 +182,57 @@ class ImportWrapper(pr.Base):
|
|||||||
# check recursion
|
# check recursion
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if self.import_path:
|
try:
|
||||||
try:
|
if self.import_path:
|
||||||
module, rest = self._importer.follow_file_system()
|
try:
|
||||||
except ModuleNotFound as e:
|
module, rest = self._importer.follow_file_system()
|
||||||
analysis.add(self._evaluator, 'import-error', e.name_part)
|
except ModuleNotFound as e:
|
||||||
return []
|
analysis.add(self._evaluator, 'import-error', e.name_part)
|
||||||
|
return []
|
||||||
|
|
||||||
if module is None:
|
if module is None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if self.import_stmt.is_nested() and not self.nested_resolve:
|
if self.import_stmt.is_nested() and not self.nested_resolve:
|
||||||
scopes = [NestedImportModule(module, self.import_stmt)]
|
scopes = [NestedImportModule(module, self.import_stmt)]
|
||||||
else:
|
|
||||||
scopes = [module]
|
|
||||||
|
|
||||||
star_imports = remove_star_imports(self._evaluator, module)
|
|
||||||
if star_imports:
|
|
||||||
scopes = [StarImportModule(scopes[0], star_imports)]
|
|
||||||
|
|
||||||
# goto only accepts Names or NameParts
|
|
||||||
if is_goto and not rest:
|
|
||||||
scopes = [s.name.names[-1] for s in scopes]
|
|
||||||
|
|
||||||
# follow the rest of the import (not FS -> classes, functions)
|
|
||||||
if len(rest) > 1 or rest and self.is_like_search:
|
|
||||||
scopes = []
|
|
||||||
if ('os', 'path') == self.import_path[:2] \
|
|
||||||
and not self._is_relative_import():
|
|
||||||
# This is a huge exception, we follow a nested import
|
|
||||||
# ``os.path``, because it's a very important one in Python
|
|
||||||
# that is being achieved by messing with ``sys.modules`` in
|
|
||||||
# ``os``.
|
|
||||||
scopes = self._evaluator.follow_path(iter(rest), [module], module)
|
|
||||||
elif rest:
|
|
||||||
if is_goto:
|
|
||||||
scopes = list(chain.from_iterable(
|
|
||||||
self._evaluator.find_types(s, rest[0], is_goto=True)
|
|
||||||
for s in scopes))
|
|
||||||
else:
|
else:
|
||||||
scopes = list(chain.from_iterable(
|
scopes = [module]
|
||||||
self._evaluator.follow_path(iter(rest), [s], s)
|
|
||||||
for s in scopes))
|
star_imports = remove_star_imports(self._evaluator, module)
|
||||||
else:
|
if star_imports:
|
||||||
scopes = [ImportWrapper.GlobalNamespace]
|
scopes = [StarImportModule(scopes[0], star_imports)]
|
||||||
debug.dbg('after import: %s', scopes)
|
|
||||||
if not scopes:
|
# goto only accepts `Name`
|
||||||
analysis.add(self._evaluator, 'import-error',
|
if is_goto and not rest:
|
||||||
self._importer.import_path[-1])
|
scopes = [s.name for s in scopes]
|
||||||
self._evaluator.recursion_detector.pop_stmt()
|
|
||||||
|
# follow the rest of the import (not FS -> classes, functions)
|
||||||
|
if len(rest) > 1 or rest and self.is_like_search:
|
||||||
|
scopes = []
|
||||||
|
if ('os', 'path') == self.import_path[:2] \
|
||||||
|
and not self._is_relative_import():
|
||||||
|
# This is a huge exception, we follow a nested import
|
||||||
|
# ``os.path``, because it's a very important one in Python
|
||||||
|
# that is being achieved by messing with ``sys.modules`` in
|
||||||
|
# ``os``.
|
||||||
|
scopes = self._evaluator.follow_path(iter(rest), [module], module)
|
||||||
|
elif rest:
|
||||||
|
if is_goto:
|
||||||
|
scopes = list(chain.from_iterable(
|
||||||
|
self._evaluator.find_types(s, rest[0], is_goto=True)
|
||||||
|
for s in scopes))
|
||||||
|
else:
|
||||||
|
scopes = list(chain.from_iterable(
|
||||||
|
self._evaluator.follow_path(iter(rest), [s], s)
|
||||||
|
for s in scopes))
|
||||||
|
else:
|
||||||
|
scopes = [ImportWrapper.GlobalNamespace]
|
||||||
|
debug.dbg('after import: %s', scopes)
|
||||||
|
if not scopes:
|
||||||
|
analysis.add(self._evaluator, 'import-error',
|
||||||
|
self._importer.import_path[-1])
|
||||||
|
finally:
|
||||||
|
self._evaluator.recursion_detector.pop_stmt()
|
||||||
return scopes
|
return scopes
|
||||||
|
|
||||||
|
|
||||||
@@ -244,12 +249,12 @@ class NestedImportModule(pr.Module):
|
|||||||
# This is not an existing Import statement. Therefore, set position to
|
# This is not an existing Import statement. Therefore, set position to
|
||||||
# 0 (0 is not a valid line number).
|
# 0 (0 is not a valid line number).
|
||||||
zero = (0, 0)
|
zero = (0, 0)
|
||||||
names = [unicode(name_part) for name_part in i.namespace.names[1:]]
|
names = [unicode(name_part) for name_part in i.namespace_names[1:]]
|
||||||
name = helpers.FakeName(names, self._nested_import)
|
name = helpers.FakeName(names, self._nested_import)
|
||||||
new = pr.Import(i._sub_module, zero, zero, name)
|
new = pr.Import(i._sub_module, zero, zero, name)
|
||||||
new.parent = self._module
|
new.parent = self._module
|
||||||
debug.dbg('Generated a nested import: %s', new)
|
debug.dbg('Generated a nested import: %s', new)
|
||||||
return helpers.FakeName(str(i.namespace.names[1]), new)
|
return helpers.FakeName(str(i.namespace_names[1]), new)
|
||||||
|
|
||||||
def _get_defined_names(self):
|
def _get_defined_names(self):
|
||||||
"""
|
"""
|
||||||
@@ -330,7 +335,7 @@ class _Importer(object):
|
|||||||
self.file_path = os.path.dirname(path) if path is not None else None
|
self.file_path = os.path.dirname(path) if path is not None else None
|
||||||
|
|
||||||
def str_import_path(self):
|
def str_import_path(self):
|
||||||
"""Returns the import path as pure strings instead of NameParts."""
|
"""Returns the import path as pure strings instead of `Name`."""
|
||||||
return tuple(str(name_part) for name_part in self.import_path)
|
return tuple(str(name_part) for name_part in self.import_path)
|
||||||
|
|
||||||
def get_relative_path(self):
|
def get_relative_path(self):
|
||||||
@@ -372,12 +377,12 @@ class _Importer(object):
|
|||||||
pos = (part._line, part._column)
|
pos = (part._line, part._column)
|
||||||
try:
|
try:
|
||||||
self.import_path = (
|
self.import_path = (
|
||||||
pr.NamePart(FakeSubModule, 'flask_' + str(part), part.parent, pos),
|
pr.Name(FakeSubModule, 'flask_' + str(part), part.parent, pos),
|
||||||
) + orig_path[3:]
|
) + orig_path[3:]
|
||||||
return self._real_follow_file_system()
|
return self._real_follow_file_system()
|
||||||
except ModuleNotFound as e:
|
except ModuleNotFound as e:
|
||||||
self.import_path = (
|
self.import_path = (
|
||||||
pr.NamePart(FakeSubModule, 'flaskext', part.parent, pos),
|
pr.Name(FakeSubModule, 'flaskext', part.parent, pos),
|
||||||
) + orig_path[2:]
|
) + orig_path[2:]
|
||||||
return self._real_follow_file_system()
|
return self._real_follow_file_system()
|
||||||
return self._real_follow_file_system()
|
return self._real_follow_file_system()
|
||||||
@@ -606,5 +611,5 @@ def get_modules_containing_name(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:
|
if c is not None and c not in mods and not isinstance(c, compiled.CompiledObject):
|
||||||
yield c
|
yield c
|
||||||
|
|||||||
@@ -193,14 +193,14 @@ class Array(use_metaclass(CachedMetaClass, IterableWrapper)):
|
|||||||
|
|
||||||
def scope_names_generator(self, position=None):
|
def scope_names_generator(self, position=None):
|
||||||
"""
|
"""
|
||||||
This method generates all `ArrayMethod` for one pr.Array.
|
|
||||||
It returns e.g. for a list: append, pop, ...
|
It returns e.g. for a list: append, pop, ...
|
||||||
"""
|
"""
|
||||||
# `array.type` is a string with the type, e.g. 'list'.
|
# `array.type` is a string with the type, e.g. 'list'.
|
||||||
scope = self._evaluator.find_types(compiled.builtin, self._array.type)[0]
|
scope = self._evaluator.find_types(compiled.builtin, self._array.type)[0]
|
||||||
scope = self._evaluator.execute(scope)[0] # builtins only have one class
|
scope = self._evaluator.execute(scope)[0] # builtins only have one class
|
||||||
|
from jedi.evaluate.representation import get_instance_el
|
||||||
for _, names in scope.scope_names_generator():
|
for _, names in scope.scope_names_generator():
|
||||||
yield self, [ArrayMethod(n) for n in names]
|
yield self, [get_instance_el(self._evaluator, self, n) for n in names]
|
||||||
|
|
||||||
@common.safe_property
|
@common.safe_property
|
||||||
def parent(self):
|
def parent(self):
|
||||||
@@ -225,34 +225,6 @@ class Array(use_metaclass(CachedMetaClass, IterableWrapper)):
|
|||||||
return "<e%s of %s>" % (type(self).__name__, self._array)
|
return "<e%s of %s>" % (type(self).__name__, self._array)
|
||||||
|
|
||||||
|
|
||||||
class ArrayMethod(IterableWrapper):
|
|
||||||
"""
|
|
||||||
A name, e.g. `list.append`, it is used to access the original array
|
|
||||||
methods.
|
|
||||||
"""
|
|
||||||
def __init__(self, name):
|
|
||||||
super(ArrayMethod, self).__init__()
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
@property
|
|
||||||
@underscore_memoization
|
|
||||||
def names(self):
|
|
||||||
# TODO remove this method, we need the ArrayMethod input to be a NamePart.
|
|
||||||
return [pr.NamePart(self.name._sub_module, unicode(n), self, n.start_pos) for n in self.name.names]
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
# Set access privileges:
|
|
||||||
if name not in ['parent', 'start_pos', 'end_pos', 'get_code', 'get_definition']:
|
|
||||||
raise AttributeError('Strange access on %s: %s.' % (self, name))
|
|
||||||
return getattr(self.name, name)
|
|
||||||
|
|
||||||
def get_parent_until(self):
|
|
||||||
return compiled.builtin
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<%s of %s>" % (type(self).__name__, self.name)
|
|
||||||
|
|
||||||
|
|
||||||
class MergedArray(Array):
|
class MergedArray(Array):
|
||||||
def __init__(self, evaluator, arrays):
|
def __init__(self, evaluator, arrays):
|
||||||
super(MergedArray, self).__init__(evaluator, arrays[-1]._array)
|
super(MergedArray, self).__init__(evaluator, arrays[-1]._array)
|
||||||
@@ -342,7 +314,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
|||||||
result = []
|
result = []
|
||||||
for c in calls:
|
for c in calls:
|
||||||
call_path = list(c.generate_call_path())
|
call_path = list(c.generate_call_path())
|
||||||
call_path_simple = [unicode(n) if isinstance(n, pr.NamePart) else n
|
call_path_simple = [unicode(n) if isinstance(n, pr.Name) else n
|
||||||
for n in call_path]
|
for n in call_path]
|
||||||
separate_index = call_path_simple.index(add_name)
|
separate_index = call_path_simple.index(add_name)
|
||||||
if add_name == call_path_simple[-1] or separate_index == 0:
|
if add_name == call_path_simple[-1] or separate_index == 0:
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import copy
|
import copy
|
||||||
|
|
||||||
from jedi._compatibility import unicode, zip_longest
|
from jedi._compatibility import unicode, zip_longest
|
||||||
|
from jedi import debug
|
||||||
|
from jedi import common
|
||||||
from jedi.parser import representation as pr
|
from jedi.parser import representation as pr
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi import common
|
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
|
|
||||||
@@ -294,7 +295,7 @@ def _iterate_star_args(evaluator, array, expression_list, func):
|
|||||||
for field_stmt in array.iter_content():
|
for field_stmt in array.iter_content():
|
||||||
yield helpers.FakeStatement([field_stmt])
|
yield helpers.FakeStatement([field_stmt])
|
||||||
elif isinstance(array, Instance) and array.name.get_code() == 'tuple':
|
elif isinstance(array, Instance) and array.name.get_code() == 'tuple':
|
||||||
pass
|
debug.warning('Ignored a tuple *args input %s' % array)
|
||||||
else:
|
else:
|
||||||
if expression_list:
|
if expression_list:
|
||||||
m = "TypeError: %s() argument after * must be a sequence, not %s" \
|
m = "TypeError: %s() argument after * must be a sequence, not %s" \
|
||||||
@@ -320,6 +321,7 @@ def _star_star_dict(evaluator, array, expression_list, func):
|
|||||||
elif isinstance(call, pr.Call):
|
elif isinstance(call, pr.Call):
|
||||||
key = call.name
|
key = call.name
|
||||||
else:
|
else:
|
||||||
|
debug.warning('Ignored complicated **kwargs stmt %s' % call)
|
||||||
continue # We ignore complicated statements here, for now.
|
continue # We ignore complicated statements here, for now.
|
||||||
|
|
||||||
# If the string is a duplicate, we don't care it's illegal Python
|
# If the string is a duplicate, we don't care it's illegal Python
|
||||||
@@ -359,8 +361,6 @@ def _gen_param_name_copy(func, var_args, param, keys=(), values=(), array_type=N
|
|||||||
new_param.set_expression_list([arr])
|
new_param.set_expression_list([arr])
|
||||||
|
|
||||||
name = copy.copy(param.get_name())
|
name = copy.copy(param.get_name())
|
||||||
name.names = [copy.copy(name.names[0])]
|
|
||||||
name.names[0].parent = name
|
|
||||||
name.parent = new_param
|
name.parent = new_param
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|||||||
@@ -164,12 +164,14 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
|
|||||||
# because to follow them and their self variables is too
|
# because to follow them and their self variables is too
|
||||||
# complicated.
|
# complicated.
|
||||||
sub = self._get_method_execution(sub)
|
sub = self._get_method_execution(sub)
|
||||||
for n in sub.get_defined_names():
|
for per_name_list in sub.get_names_dict().values():
|
||||||
# Only names with the selfname are being added.
|
for call in per_name_list:
|
||||||
# It is also important, that they have a len() of 2,
|
if unicode(call.name) == self_name \
|
||||||
# because otherwise, they are just something else
|
and isinstance(call.next, pr.Call) \
|
||||||
if unicode(n.names[0]) == self_name and len(n.names) == 2:
|
and call.next.next is None:
|
||||||
add_self_dot_name(n)
|
names.append(get_instance_el(self._evaluator, self, call.next.name))
|
||||||
|
#if unicode(n.names[0]) == self_name and len(n.names) == 2:
|
||||||
|
# add_self_dot_name(n)
|
||||||
|
|
||||||
for s in self.base.py__bases__(self._evaluator):
|
for s in self.base.py__bases__(self._evaluator):
|
||||||
if not isinstance(s, compiled.CompiledObject):
|
if not isinstance(s, compiled.CompiledObject):
|
||||||
@@ -243,7 +245,12 @@ def get_instance_el(evaluator, instance, var, is_class_var=False):
|
|||||||
untouched.
|
untouched.
|
||||||
"""
|
"""
|
||||||
if isinstance(var, (Instance, compiled.CompiledObject, pr.Operator, Token,
|
if isinstance(var, (Instance, compiled.CompiledObject, pr.Operator, Token,
|
||||||
pr.Module, FunctionExecution)):
|
pr.Module, FunctionExecution, pr.Name)):
|
||||||
|
if isinstance(var, pr.Name):
|
||||||
|
# TODO temp solution, remove later, Name should never get
|
||||||
|
# here?
|
||||||
|
par = get_instance_el(evaluator, instance, var.parent, is_class_var)
|
||||||
|
return pr.Name(var._sub_module, unicode(var), par, var.start_pos)
|
||||||
return var
|
return var
|
||||||
|
|
||||||
var = wrap(evaluator, var)
|
var = wrap(evaluator, var)
|
||||||
@@ -275,6 +282,9 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)):
|
|||||||
return par
|
return par
|
||||||
|
|
||||||
def get_parent_until(self, *args, **kwargs):
|
def get_parent_until(self, *args, **kwargs):
|
||||||
|
if isinstance(self.var, pr.Name):
|
||||||
|
# TODO Name should never even be InstanceElements
|
||||||
|
return pr.Simple.get_parent_until(self.parent, *args, **kwargs)
|
||||||
return pr.Simple.get_parent_until(self, *args, **kwargs)
|
return pr.Simple.get_parent_until(self, *args, **kwargs)
|
||||||
|
|
||||||
def get_definition(self):
|
def get_definition(self):
|
||||||
@@ -291,12 +301,6 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)):
|
|||||||
return [get_instance_el(self._evaluator, self.instance, command, self.is_class_var)
|
return [get_instance_el(self._evaluator, self.instance, command, self.is_class_var)
|
||||||
for command in self.var.expression_list()]
|
for command in self.var.expression_list()]
|
||||||
|
|
||||||
@property
|
|
||||||
@underscore_memoization
|
|
||||||
def names(self):
|
|
||||||
return [pr.NamePart(helpers.FakeSubModule, unicode(n), self, n.start_pos)
|
|
||||||
for n in self.var.names]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@underscore_memoization
|
@underscore_memoization
|
||||||
def name(self):
|
def name(self):
|
||||||
@@ -390,6 +394,9 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)):
|
|||||||
def py__call__(self, evaluator, params):
|
def py__call__(self, evaluator, params):
|
||||||
return [Instance(evaluator, self, params)]
|
return [Instance(evaluator, self, params)]
|
||||||
|
|
||||||
|
def py__getattribute__(self, name):
|
||||||
|
return self._evaluator.find_types(self, name)
|
||||||
|
|
||||||
def scope_names_generator(self, position=None, add_class_vars=True):
|
def scope_names_generator(self, position=None, add_class_vars=True):
|
||||||
def in_iterable(name, iterable):
|
def in_iterable(name, iterable):
|
||||||
""" checks if the name is in the variable 'iterable'. """
|
""" checks if the name is in the variable 'iterable'. """
|
||||||
@@ -522,6 +529,21 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
|
|||||||
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
|
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
|
||||||
|
|
||||||
|
|
||||||
|
class LazyDict(object):
|
||||||
|
def __init__(self, old_dct, copy_func):
|
||||||
|
self._copy_func = copy_func
|
||||||
|
self._old_dct = old_dct
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._copy_func(self._old_dct[key])
|
||||||
|
|
||||||
|
@underscore_memoization
|
||||||
|
def values(self):
|
||||||
|
# TODO REMOVE this. Not necessary with correct name lookups.
|
||||||
|
for calls in self._old_dct.values():
|
||||||
|
yield self._copy_func(calls)
|
||||||
|
|
||||||
|
|
||||||
class FunctionExecution(Executed):
|
class FunctionExecution(Executed):
|
||||||
"""
|
"""
|
||||||
This class is used to evaluate functions and their returns.
|
This class is used to evaluate functions and their returns.
|
||||||
@@ -570,6 +592,10 @@ class FunctionExecution(Executed):
|
|||||||
break
|
break
|
||||||
return types
|
return types
|
||||||
|
|
||||||
|
@underscore_memoization
|
||||||
|
def get_names_dict(self):
|
||||||
|
return LazyDict(self.base.get_names_dict(), self._copy_list)
|
||||||
|
|
||||||
@memoize_default(default=())
|
@memoize_default(default=())
|
||||||
def _get_params(self):
|
def _get_params(self):
|
||||||
"""
|
"""
|
||||||
@@ -591,15 +617,13 @@ class FunctionExecution(Executed):
|
|||||||
names = pr.filter_after_position(pr.Scope.get_defined_names(self), position)
|
names = pr.filter_after_position(pr.Scope.get_defined_names(self), position)
|
||||||
yield self, self._get_params() + names
|
yield self, self._get_params() + names
|
||||||
|
|
||||||
def _copy_list(self, list_name):
|
def _copy_list(self, lst):
|
||||||
"""
|
"""
|
||||||
Copies a list attribute of a parser Function. Copying is very
|
Copies a list attribute of a parser Function. Copying is very
|
||||||
expensive, because it is something like `copy.deepcopy`. However, these
|
expensive, because it is something like `copy.deepcopy`. However, these
|
||||||
copied objects can be used for the executions, as if they were in the
|
copied objects can be used for the executions, as if they were in the
|
||||||
execution.
|
execution.
|
||||||
"""
|
"""
|
||||||
# Copy all these lists into this local function.
|
|
||||||
lst = getattr(self.base, list_name)
|
|
||||||
objects = []
|
objects = []
|
||||||
for element in lst:
|
for element in lst:
|
||||||
self._scope_copy(element.parent)
|
self._scope_copy(element.parent)
|
||||||
@@ -622,22 +646,22 @@ class FunctionExecution(Executed):
|
|||||||
@common.safe_property
|
@common.safe_property
|
||||||
@memoize_default([])
|
@memoize_default([])
|
||||||
def returns(self):
|
def returns(self):
|
||||||
return self._copy_list('returns')
|
return self._copy_list(self.base.returns)
|
||||||
|
|
||||||
@common.safe_property
|
@common.safe_property
|
||||||
@memoize_default([])
|
@memoize_default([])
|
||||||
def asserts(self):
|
def asserts(self):
|
||||||
return self._copy_list('asserts')
|
return self._copy_list(self.base.asserts)
|
||||||
|
|
||||||
@common.safe_property
|
@common.safe_property
|
||||||
@memoize_default([])
|
@memoize_default([])
|
||||||
def statements(self):
|
def statements(self):
|
||||||
return self._copy_list('statements')
|
return self._copy_list(self.base.statements)
|
||||||
|
|
||||||
@common.safe_property
|
@common.safe_property
|
||||||
@memoize_default([])
|
@memoize_default([])
|
||||||
def subscopes(self):
|
def subscopes(self):
|
||||||
return self._copy_list('subscopes')
|
return self._copy_list(self.base.subscopes)
|
||||||
|
|
||||||
def get_statement_for_position(self, pos):
|
def get_statement_for_position(self, pos):
|
||||||
return pr.Scope.get_statement_for_position(self, pos)
|
return pr.Scope.get_statement_for_position(self, pos)
|
||||||
@@ -667,6 +691,11 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
|||||||
# All the additional module attributes are strings.
|
# All the additional module attributes are strings.
|
||||||
return [helpers.LazyName(n, parent_callback) for n in names]
|
return [helpers.LazyName(n, parent_callback) for n in names]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@memoize_default()
|
||||||
|
def name(self):
|
||||||
|
return pr.Name(self, unicode(self.base.name), self, (1, 0))
|
||||||
|
|
||||||
@memoize_default()
|
@memoize_default()
|
||||||
def _sub_modules(self):
|
def _sub_modules(self):
|
||||||
"""
|
"""
|
||||||
@@ -683,6 +712,14 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
|||||||
imp = helpers.FakeImport(name, self, level=1)
|
imp = helpers.FakeImport(name, self, level=1)
|
||||||
name.parent = imp
|
name.parent = imp
|
||||||
names.append(name)
|
names.append(name)
|
||||||
|
|
||||||
|
# TODO add something like this in the future, its cleaner than the
|
||||||
|
# import hacks.
|
||||||
|
# ``os.path`` is a hardcoded exception, because it's a
|
||||||
|
# ``sys.modules`` modification.
|
||||||
|
#if str(self.name) == 'os':
|
||||||
|
# names.append(helpers.FakeName('path', parent=self))
|
||||||
|
|
||||||
return names
|
return names
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def _paths_from_assignment(evaluator, statement):
|
|||||||
for exp_list, operator in statement.assignment_details:
|
for exp_list, operator in statement.assignment_details:
|
||||||
if len(exp_list) != 1 or not isinstance(exp_list[0], pr.Call):
|
if len(exp_list) != 1 or not isinstance(exp_list[0], pr.Call):
|
||||||
continue
|
continue
|
||||||
if unicode(exp_list[0].name) != 'sys.path':
|
if exp_list[0].names() != ['sys', 'path']:
|
||||||
continue
|
continue
|
||||||
# TODO at this point we ignore all ways what could be assigned to
|
# TODO at this point we ignore all ways what could be assigned to
|
||||||
# sys.path or an execution of it. Here we could do way more
|
# sys.path or an execution of it. Here we could do way more
|
||||||
@@ -88,17 +88,15 @@ def _paths_from_insert(module_path, exe):
|
|||||||
|
|
||||||
def _paths_from_call_expression(module_path, call):
|
def _paths_from_call_expression(module_path, call):
|
||||||
""" extract the path from either "sys.path.append" or "sys.path.insert" """
|
""" extract the path from either "sys.path.append" or "sys.path.insert" """
|
||||||
if not call.next_is_execution():
|
names = call.names()
|
||||||
return
|
if names[:3] != ['sys', 'path', 'append'] and names[:3] != ['sys', 'path', 'insert']:
|
||||||
|
return []
|
||||||
|
if not call.next.next.next_is_execution():
|
||||||
|
return []
|
||||||
|
|
||||||
n = call.name
|
|
||||||
if not isinstance(n, pr.Name) or len(n.names) != 3:
|
|
||||||
return
|
|
||||||
names = [unicode(x) for x in n.names]
|
|
||||||
if names[:2] != ['sys', 'path']:
|
|
||||||
return
|
|
||||||
cmd = names[2]
|
cmd = names[2]
|
||||||
exe = call.next
|
exe = call.next.next.next
|
||||||
|
path = None
|
||||||
if cmd == 'insert' and len(exe) == 2:
|
if cmd == 'insert' and len(exe) == 2:
|
||||||
path = _paths_from_insert(module_path, exe)
|
path = _paths_from_insert(module_path, exe)
|
||||||
elif cmd == 'append' and len(exe) == 1:
|
elif cmd == 'append' and len(exe) == 1:
|
||||||
|
|||||||
+45
-45
@@ -100,38 +100,46 @@ class Parser(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
self.module.used_names[tok_name] = set([simple])
|
self.module.used_names[tok_name] = set([simple])
|
||||||
self.module.temp_used_names = []
|
self.module.temp_used_names = []
|
||||||
|
if isinstance(simple, pr.Statement):
|
||||||
|
for name, calls in simple.get_names_dict().items():
|
||||||
|
self._scope.add_name_calls(name, calls)
|
||||||
|
|
||||||
def _parse_dot_name(self, pre_used_token=None):
|
def _parse_dotted_name(self, pre_used_token=None):
|
||||||
"""
|
"""
|
||||||
The dot name parser parses a name, variable or function and returns
|
The dot name parser parses a name, variable or function and returns
|
||||||
their names.
|
their names.
|
||||||
|
Just used for parsing imports.
|
||||||
|
|
||||||
:return: tuple of Name, next_token
|
:return: tuple of Name, next_token
|
||||||
"""
|
"""
|
||||||
def append(el):
|
def append(tok):
|
||||||
names.append(el)
|
names.append(pr.Name(self.module, tok.string, None, tok.start_pos))
|
||||||
self.module.temp_used_names.append(el[0])
|
self.module.temp_used_names.append(tok.string)
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
tok = next(self._gen) if pre_used_token is None else pre_used_token
|
tok = next(self._gen) if pre_used_token is None else pre_used_token
|
||||||
|
|
||||||
if tok.type != tokenize.NAME and tok.string != '*':
|
if tok.type != tokenize.NAME and tok.string != '*':
|
||||||
return None, tok
|
return [], tok
|
||||||
|
|
||||||
first_pos = tok.start_pos
|
append(tok)
|
||||||
append((tok.string, first_pos))
|
|
||||||
while True:
|
while True:
|
||||||
end_pos = tok.end_pos
|
|
||||||
tok = next(self._gen)
|
tok = next(self._gen)
|
||||||
if tok.string != '.':
|
if tok.string != '.':
|
||||||
break
|
break
|
||||||
tok = next(self._gen)
|
tok = next(self._gen)
|
||||||
if tok.type != tokenize.NAME:
|
if tok.type != tokenize.NAME:
|
||||||
break
|
break
|
||||||
append((tok.string, tok.start_pos))
|
append(tok)
|
||||||
|
|
||||||
n = pr.Name(self.module, names, first_pos, end_pos) if names else None
|
return names, tok
|
||||||
return n, tok
|
|
||||||
|
def _parse_name(self, pre_used_token=None):
|
||||||
|
tok = next(self._gen) if pre_used_token is None else pre_used_token
|
||||||
|
self.module.temp_used_names.append(tok.string)
|
||||||
|
if tok.type != tokenize.NAME:
|
||||||
|
return None, tok
|
||||||
|
return pr.Name(self.module, tok.string, None, tok.start_pos), next(self._gen)
|
||||||
|
|
||||||
def _parse_import_list(self):
|
def _parse_import_list(self):
|
||||||
"""
|
"""
|
||||||
@@ -161,13 +169,13 @@ class Parser(object):
|
|||||||
tok = next(self._gen)
|
tok = next(self._gen)
|
||||||
if brackets and tok.type == tokenize.NEWLINE:
|
if brackets and tok.type == tokenize.NEWLINE:
|
||||||
tok = next(self._gen)
|
tok = next(self._gen)
|
||||||
i, tok = self._parse_dot_name(tok)
|
names, tok = self._parse_dotted_name(tok)
|
||||||
if not i:
|
if not names:
|
||||||
defunct = True
|
defunct = True
|
||||||
name2 = None
|
alias = None
|
||||||
if tok.string == 'as':
|
if tok.string == 'as':
|
||||||
name2, tok = self._parse_dot_name()
|
alias, tok = self._parse_name()
|
||||||
imports.append((i, name2, defunct))
|
imports.append((names, alias, defunct))
|
||||||
while tok.string not in continue_kw:
|
while tok.string not in continue_kw:
|
||||||
tok = next(self._gen)
|
tok = next(self._gen)
|
||||||
if not (tok.string == "," or brackets and tok.type == tokenize.NEWLINE):
|
if not (tok.string == "," or brackets and tok.type == tokenize.NEWLINE):
|
||||||
@@ -224,10 +232,8 @@ class Parser(object):
|
|||||||
if tok.type != tokenize.NAME:
|
if tok.type != tokenize.NAME:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
fname = pr.Name(self.module, [(tok.string, tok.start_pos)], tok.start_pos,
|
fname, tok = self._parse_name(tok)
|
||||||
tok.end_pos)
|
|
||||||
|
|
||||||
tok = next(self._gen)
|
|
||||||
if tok.string != '(':
|
if tok.string != '(':
|
||||||
return None
|
return None
|
||||||
params = self._parse_parentheses(is_class=False)
|
params = self._parse_parentheses(is_class=False)
|
||||||
@@ -246,7 +252,7 @@ class Parser(object):
|
|||||||
if colon.string != ':':
|
if colon.string != ':':
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# because of 2 line func param definitions
|
# Because of 2 line func param definitions
|
||||||
return pr.Function(self.module, fname, params, first_pos, annotation)
|
return pr.Function(self.module, fname, params, first_pos, annotation)
|
||||||
|
|
||||||
def _parse_class(self):
|
def _parse_class(self):
|
||||||
@@ -264,11 +270,9 @@ class Parser(object):
|
|||||||
cname.start_pos[0], tokenize.tok_name[cname.type], cname.string)
|
cname.start_pos[0], tokenize.tok_name[cname.type], cname.string)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cname = pr.Name(self.module, [(cname.string, cname.start_pos)],
|
cname, _next = self._parse_name(cname)
|
||||||
cname.start_pos, cname.end_pos)
|
|
||||||
|
|
||||||
superclasses = []
|
superclasses = []
|
||||||
_next = next(self._gen)
|
|
||||||
if _next.string == '(':
|
if _next.string == '(':
|
||||||
superclasses = self._parse_parentheses(is_class=True)
|
superclasses = self._parse_parentheses(is_class=True)
|
||||||
_next = next(self._gen)
|
_next = next(self._gen)
|
||||||
@@ -342,7 +346,7 @@ class Parser(object):
|
|||||||
if tok.string == 'as':
|
if tok.string == 'as':
|
||||||
tok = next(self._gen)
|
tok = next(self._gen)
|
||||||
if tok.type == tokenize.NAME:
|
if tok.type == tokenize.NAME:
|
||||||
n, tok = self._parse_dot_name(self._gen.current)
|
n, tok = self._parse_name(self._gen.current)
|
||||||
if n:
|
if n:
|
||||||
set_vars.append(n)
|
set_vars.append(n)
|
||||||
as_names.append(n)
|
as_names.append(n)
|
||||||
@@ -354,11 +358,7 @@ class Parser(object):
|
|||||||
elif in_lambda_param and tok.string == ':':
|
elif in_lambda_param and tok.string == ':':
|
||||||
in_lambda_param = False
|
in_lambda_param = False
|
||||||
elif tok.type == tokenize.NAME and not is_kw:
|
elif tok.type == tokenize.NAME and not is_kw:
|
||||||
n, tok = self._parse_dot_name(self._gen.current)
|
tok_list[-1], tok = self._parse_name(tok)
|
||||||
# removed last entry, because we add Name
|
|
||||||
tok_list.pop()
|
|
||||||
if n:
|
|
||||||
tok_list.append(n)
|
|
||||||
continue
|
continue
|
||||||
elif tok.string in opening_brackets:
|
elif tok.string in opening_brackets:
|
||||||
level += 1
|
level += 1
|
||||||
@@ -460,10 +460,10 @@ class Parser(object):
|
|||||||
# import stuff
|
# import stuff
|
||||||
elif tok_str == 'import':
|
elif tok_str == 'import':
|
||||||
imports = self._parse_import_list()
|
imports = self._parse_import_list()
|
||||||
for count, (m, alias, defunct) in enumerate(imports):
|
for count, (names, alias, defunct) in enumerate(imports):
|
||||||
e = (alias or m or self._gen.previous).end_pos
|
e = (alias or names and names[-1] or self._gen.previous).end_pos
|
||||||
end_pos = self._gen.previous.end_pos if count + 1 == len(imports) else e
|
end_pos = self._gen.previous.end_pos if count + 1 == len(imports) else e
|
||||||
i = pr.Import(self.module, first_pos, end_pos, m,
|
i = pr.Import(self.module, first_pos, end_pos, names,
|
||||||
alias, defunct=defunct)
|
alias, defunct=defunct)
|
||||||
self._check_user_stmt(i)
|
self._check_user_stmt(i)
|
||||||
self._scope.add_import(i)
|
self._scope.add_import(i)
|
||||||
@@ -482,26 +482,26 @@ class Parser(object):
|
|||||||
break
|
break
|
||||||
relative_count += 1
|
relative_count += 1
|
||||||
# the from import
|
# the from import
|
||||||
mod, tok = self._parse_dot_name(self._gen.current)
|
from_names, tok = self._parse_dotted_name(self._gen.current)
|
||||||
tok_str = tok.string
|
tok_str = tok.string
|
||||||
if str(mod) == 'import' and relative_count:
|
if len(from_names) == 1 and str(from_names[0]) == 'import' and relative_count:
|
||||||
self._gen.push_last_back()
|
self._gen.push_last_back()
|
||||||
tok_str = 'import'
|
tok_str = 'import'
|
||||||
mod = None
|
from_names = []
|
||||||
if not mod and not relative_count or tok_str != "import":
|
if not from_names and not relative_count or tok_str != "import":
|
||||||
debug.warning("from: syntax error@%s", tok.start_pos[0])
|
debug.warning("from: syntax error@%s", tok.start_pos[0])
|
||||||
defunct = True
|
defunct = True
|
||||||
if tok_str != 'import':
|
if tok_str != 'import':
|
||||||
self._gen.push_last_back()
|
self._gen.push_last_back()
|
||||||
names = self._parse_import_list()
|
imports = self._parse_import_list()
|
||||||
for count, (name, alias, defunct2) in enumerate(names):
|
for count, (names, alias, defunct2) in enumerate(imports):
|
||||||
star = name is not None and unicode(name.names[0]) == '*'
|
star = names and unicode(names[-1]) == '*'
|
||||||
if star:
|
if star:
|
||||||
name = None
|
names = []
|
||||||
e = (alias or name or self._gen.previous).end_pos
|
e = (alias or names and names[-1] or self._gen.previous).end_pos
|
||||||
end_pos = self._gen.previous.end_pos if count + 1 == len(names) else e
|
#end_pos = self._gen.previous.end_pos if count + 1 == len(names) else e
|
||||||
i = pr.Import(self.module, first_pos, end_pos, name,
|
i = pr.Import(self.module, first_pos, e, names,
|
||||||
alias, mod, star, relative_count,
|
alias, from_names, star, relative_count,
|
||||||
defunct=defunct or defunct2)
|
defunct=defunct or defunct2)
|
||||||
self._check_user_stmt(i)
|
self._check_user_stmt(i)
|
||||||
self._scope.add_import(i)
|
self._scope.add_import(i)
|
||||||
@@ -536,7 +536,7 @@ class Parser(object):
|
|||||||
if command == 'except' and tok.string == ',':
|
if command == 'except' and tok.string == ',':
|
||||||
# the except statement defines a var
|
# the except statement defines a var
|
||||||
# this is only true for python 2
|
# this is only true for python 2
|
||||||
n, tok = self._parse_dot_name()
|
n, tok = self._parse_name()
|
||||||
if n:
|
if n:
|
||||||
n.parent = statement
|
n.parent = statement
|
||||||
statement.as_names.append(n)
|
statement.as_names.append(n)
|
||||||
|
|||||||
+130
-107
@@ -37,6 +37,7 @@ See also :attr:`Scope.subscopes` and :attr:`Scope.statements`.
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from inspect import cleandoc
|
from inspect import cleandoc
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from jedi._compatibility import (next, Python3Method, encoding, unicode,
|
from jedi._compatibility import (next, Python3Method, encoding, unicode,
|
||||||
is_py3, u, literal_eval, use_metaclass)
|
is_py3, u, literal_eval, use_metaclass)
|
||||||
@@ -231,6 +232,14 @@ class IsScope(use_metaclass(IsScopeMeta)):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _return_empty_list():
|
||||||
|
"""
|
||||||
|
Necessary for pickling. It needs to be reachable for pickle, cannot
|
||||||
|
be a lambda or a closure.
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class Scope(Simple, DocstringMixin):
|
class Scope(Simple, DocstringMixin):
|
||||||
"""
|
"""
|
||||||
Super class for the parser tree, which represents the state of a python
|
Super class for the parser tree, which represents the state of a python
|
||||||
@@ -243,7 +252,7 @@ class Scope(Simple, DocstringMixin):
|
|||||||
:type start_pos: tuple(int, int)
|
:type start_pos: tuple(int, int)
|
||||||
"""
|
"""
|
||||||
__slots__ = ('subscopes', 'imports', 'statements', '_doc_token', 'asserts',
|
__slots__ = ('subscopes', 'imports', 'statements', '_doc_token', 'asserts',
|
||||||
'returns', 'is_generator')
|
'returns', 'is_generator', '_names_dict')
|
||||||
|
|
||||||
def __init__(self, module, start_pos):
|
def __init__(self, module, start_pos):
|
||||||
super(Scope, self).__init__(module, start_pos)
|
super(Scope, self).__init__(module, start_pos)
|
||||||
@@ -255,11 +264,19 @@ class Scope(Simple, DocstringMixin):
|
|||||||
# Needed here for fast_parser, because the fast_parser splits and
|
# Needed here for fast_parser, because the fast_parser splits and
|
||||||
# returns will be in "normal" modules.
|
# returns will be in "normal" modules.
|
||||||
self.returns = []
|
self.returns = []
|
||||||
|
self._names_dict = defaultdict(_return_empty_list)
|
||||||
self.is_generator = False
|
self.is_generator = False
|
||||||
|
|
||||||
def is_scope(self):
|
def is_scope(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def add_name_calls(self, name, calls):
|
||||||
|
"""Add a name to the names_dict."""
|
||||||
|
self._names_dict[name] += calls
|
||||||
|
|
||||||
|
def get_names_dict(self):
|
||||||
|
return self._names_dict
|
||||||
|
|
||||||
def add_scope(self, sub, decorators):
|
def add_scope(self, sub, decorators):
|
||||||
sub.parent = self.use_as_parent
|
sub.parent = self.use_as_parent
|
||||||
sub.decorators = decorators
|
sub.decorators = decorators
|
||||||
@@ -468,8 +485,7 @@ class SubModule(Scope, Module):
|
|||||||
string = re.sub('\.[a-z]+-\d{2}[mud]{0,3}$', '', r.group(1))
|
string = re.sub('\.[a-z]+-\d{2}[mud]{0,3}$', '', r.group(1))
|
||||||
# Positions are not real, but a module starts at (1, 0)
|
# Positions are not real, but a module starts at (1, 0)
|
||||||
p = (1, 0)
|
p = (1, 0)
|
||||||
names = [(string, p)]
|
return Name(self, string, self.use_as_parent, p)
|
||||||
return Name(self, names, p, p, self.use_as_parent)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_explicit_absolute_import(self):
|
def has_explicit_absolute_import(self):
|
||||||
@@ -478,10 +494,10 @@ class SubModule(Scope, Module):
|
|||||||
is a ``__future__`` import.
|
is a ``__future__`` import.
|
||||||
"""
|
"""
|
||||||
for imp in self.imports:
|
for imp in self.imports:
|
||||||
if imp.from_ns is None or imp.namespace is None:
|
if not imp.from_names or not imp.namespace_names:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
namespace, feature = imp.from_ns.names[0], imp.namespace.names[0]
|
namespace, feature = imp.from_names[0], imp.namespace_names[0]
|
||||||
if unicode(namespace) == "__future__" and unicode(feature) == "absolute_import":
|
if unicode(namespace) == "__future__" and unicode(feature) == "absolute_import":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -529,9 +545,9 @@ class Class(Scope):
|
|||||||
if self._doc_token is not None:
|
if self._doc_token is not None:
|
||||||
docstr = self.raw_doc
|
docstr = self.raw_doc
|
||||||
for sub in self.subscopes:
|
for sub in self.subscopes:
|
||||||
if unicode(sub.name.names[-1]) == '__init__':
|
if unicode(sub.name) == '__init__':
|
||||||
return '%s\n\n%s' % (
|
return '%s\n\n%s' % (
|
||||||
sub.get_call_signature(funcname=self.name.names[-1]), docstr)
|
sub.get_call_signature(funcname=self.name), docstr)
|
||||||
return docstr
|
return docstr
|
||||||
|
|
||||||
def scope_names_generator(self, position=None):
|
def scope_names_generator(self, position=None):
|
||||||
@@ -597,7 +613,7 @@ class Function(Scope):
|
|||||||
|
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
l = unicode(funcname or self.name.names[-1]) + '('
|
l = unicode(funcname or self.name) + '('
|
||||||
lines = []
|
lines = []
|
||||||
for (i, p) in enumerate(self.params):
|
for (i, p) in enumerate(self.params):
|
||||||
code = p.get_code(False)
|
code = p.get_code(False)
|
||||||
@@ -672,6 +688,15 @@ class Flow(Scope):
|
|||||||
s.parent = self.use_as_parent
|
s.parent = self.use_as_parent
|
||||||
self.set_vars = []
|
self.set_vars = []
|
||||||
|
|
||||||
|
def add_name_calls(self, name, calls):
|
||||||
|
"""Add a name to the names_dict."""
|
||||||
|
parent = self.parent
|
||||||
|
if isinstance(parent, Module):
|
||||||
|
# TODO this also looks like code smell. Look for opportunities to
|
||||||
|
# remove.
|
||||||
|
parent = self._sub_module
|
||||||
|
parent.add_name_calls(name, calls)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self):
|
def parent(self):
|
||||||
return self._parent
|
return self._parent
|
||||||
@@ -769,27 +794,28 @@ class Import(Simple):
|
|||||||
|
|
||||||
:param start_pos: Position (line, column) of the Import.
|
:param start_pos: Position (line, column) of the Import.
|
||||||
:type start_pos: tuple(int, int)
|
:type start_pos: tuple(int, int)
|
||||||
:param namespace: The import, can be empty if a star is given
|
:param namespace_names: The import, can be empty if a star is given
|
||||||
:type namespace: Name
|
:type namespace_names: list of Name
|
||||||
:param alias: The alias of a namespace(valid in the current namespace).
|
:param alias: The alias of a namespace(valid in the current namespace).
|
||||||
:type alias: Name
|
:type alias: list of Name
|
||||||
:param from_ns: Like the namespace, can be equally used.
|
:param from_names: Like the namespace, can be equally used.
|
||||||
:type from_ns: Name
|
:type from_names: list of Name
|
||||||
:param star: If a star is used -> from time import *.
|
:param star: If a star is used -> from time import *.
|
||||||
:type star: bool
|
:type star: bool
|
||||||
:param defunct: An Import is valid or not.
|
:param defunct: An Import is valid or not.
|
||||||
:type defunct: bool
|
:type defunct: bool
|
||||||
"""
|
"""
|
||||||
def __init__(self, module, start_pos, end_pos, namespace, alias=None,
|
def __init__(self, module, start_pos, end_pos, namespace_names, alias=None,
|
||||||
from_ns=None, star=False, relative_count=0, defunct=False):
|
from_names=(), star=False, relative_count=0, defunct=False):
|
||||||
super(Import, self).__init__(module, start_pos, end_pos)
|
super(Import, self).__init__(module, start_pos, end_pos)
|
||||||
|
|
||||||
self.namespace = namespace
|
self.namespace_names = namespace_names
|
||||||
self.alias = alias
|
self.alias = alias
|
||||||
self.from_ns = from_ns
|
if self.alias:
|
||||||
for n in namespace, alias, from_ns:
|
alias.parent = self
|
||||||
if n:
|
self.from_names = from_names
|
||||||
n.parent = self.use_as_parent
|
for n in namespace_names + list(from_names):
|
||||||
|
n.parent = self.use_as_parent
|
||||||
|
|
||||||
self.star = star
|
self.star = star
|
||||||
self.relative_count = relative_count
|
self.relative_count = relative_count
|
||||||
@@ -798,20 +824,18 @@ class Import(Simple):
|
|||||||
def get_code(self, new_line=True):
|
def get_code(self, new_line=True):
|
||||||
# in case one of the names is None
|
# in case one of the names is None
|
||||||
alias = self.alias or ''
|
alias = self.alias or ''
|
||||||
namespace = self.namespace or ''
|
|
||||||
from_ns = self.from_ns or ''
|
|
||||||
|
|
||||||
|
ns_str = '.'.join(unicode(n) for n in self.namespace_names)
|
||||||
if self.alias:
|
if self.alias:
|
||||||
ns_str = "%s as %s" % (namespace, alias)
|
ns_str = "%s as %s" % (ns_str, alias)
|
||||||
else:
|
|
||||||
ns_str = unicode(namespace)
|
|
||||||
|
|
||||||
nl = '\n' if new_line else ''
|
nl = '\n' if new_line else ''
|
||||||
if self.from_ns or self.relative_count:
|
if self.from_names or self.relative_count:
|
||||||
if self.star:
|
if self.star:
|
||||||
ns_str = '*'
|
ns_str = '*'
|
||||||
dots = '.' * self.relative_count
|
dots = '.' * self.relative_count
|
||||||
return "from %s%s import %s%s" % (dots, from_ns, ns_str, nl)
|
from_txt = '.'.join(unicode(n) for n in self.from_names)
|
||||||
|
return "from %s%s import %s%s" % (dots, from_txt, ns_str, nl)
|
||||||
else:
|
else:
|
||||||
return "import %s%s" % (ns_str, nl)
|
return "import %s%s" % (ns_str, nl)
|
||||||
|
|
||||||
@@ -822,21 +846,18 @@ class Import(Simple):
|
|||||||
return [self]
|
return [self]
|
||||||
if self.alias:
|
if self.alias:
|
||||||
return [self.alias]
|
return [self.alias]
|
||||||
if len(self.namespace) > 1:
|
if len(self.namespace_names) > 1:
|
||||||
o = self.namespace
|
return [self.namespace_names[0]]
|
||||||
n = Name(self._sub_module, [(unicode(o.names[0]), o.start_pos)],
|
|
||||||
o.start_pos, o.end_pos, parent=o.parent)
|
|
||||||
return [n]
|
|
||||||
else:
|
else:
|
||||||
return [self.namespace]
|
return self.namespace_names
|
||||||
|
|
||||||
def get_all_import_names(self):
|
def get_all_import_names(self):
|
||||||
n = []
|
n = []
|
||||||
if self.from_ns:
|
if self.from_names:
|
||||||
n.append(self.from_ns)
|
n += self.from_names
|
||||||
if self.namespace:
|
if self.namespace_names:
|
||||||
n.append(self.namespace)
|
n += self.namespace_names
|
||||||
if self.alias:
|
if self.alias is not None:
|
||||||
n.append(self.alias)
|
n.append(self.alias)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
@@ -847,8 +868,8 @@ class Import(Simple):
|
|||||||
|
|
||||||
import foo.bar
|
import foo.bar
|
||||||
"""
|
"""
|
||||||
return not self.alias and not self.from_ns and self.namespace is not None \
|
return not self.alias and not self.from_names \
|
||||||
and len(self.namespace.names) > 1
|
and len(self.namespace_names) > 1
|
||||||
|
|
||||||
|
|
||||||
class KeywordStatement(Base):
|
class KeywordStatement(Base):
|
||||||
@@ -911,9 +932,6 @@ class Statement(Simple, DocstringMixin):
|
|||||||
self._token_list = token_list
|
self._token_list = token_list
|
||||||
self._names_are_set_vars = names_are_set_vars
|
self._names_are_set_vars = names_are_set_vars
|
||||||
if set_name_parents:
|
if set_name_parents:
|
||||||
for t in token_list:
|
|
||||||
if isinstance(t, Name):
|
|
||||||
t.parent = self.use_as_parent
|
|
||||||
for n in as_names:
|
for n in as_names:
|
||||||
n.parent = self.use_as_parent
|
n.parent = self.use_as_parent
|
||||||
self._doc_token = None
|
self._doc_token = None
|
||||||
@@ -950,7 +968,7 @@ class Statement(Simple, DocstringMixin):
|
|||||||
return code
|
return code
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
""" Get the names for the statement. """
|
"""Get the names for the statement."""
|
||||||
if self._set_vars is None:
|
if self._set_vars is None:
|
||||||
|
|
||||||
def search_calls(calls):
|
def search_calls(calls):
|
||||||
@@ -959,15 +977,11 @@ class Statement(Simple, DocstringMixin):
|
|||||||
for stmt in call:
|
for stmt in call:
|
||||||
search_calls(stmt.expression_list())
|
search_calls(stmt.expression_list())
|
||||||
elif isinstance(call, Call):
|
elif isinstance(call, Call):
|
||||||
c = call
|
|
||||||
# Check if there's an execution in it, if so this is
|
# Check if there's an execution in it, if so this is
|
||||||
# not a set_var.
|
# not a set_var.
|
||||||
while c:
|
if not call.next:
|
||||||
if isinstance(c.next, Array):
|
|
||||||
break
|
|
||||||
c = c.next
|
|
||||||
else:
|
|
||||||
self._set_vars.append(call.name)
|
self._set_vars.append(call.name)
|
||||||
|
continue
|
||||||
|
|
||||||
self._set_vars = []
|
self._set_vars = []
|
||||||
for calls, operation in self.assignment_details:
|
for calls, operation in self.assignment_details:
|
||||||
@@ -978,6 +992,37 @@ class Statement(Simple, DocstringMixin):
|
|||||||
search_calls(self.expression_list())
|
search_calls(self.expression_list())
|
||||||
return self._set_vars + self.as_names
|
return self._set_vars + self.as_names
|
||||||
|
|
||||||
|
def get_names_dict(self):
|
||||||
|
"""The future of name resolution. Returns a dict(str -> Call)."""
|
||||||
|
dct = defaultdict(lambda: [])
|
||||||
|
|
||||||
|
def search_calls(calls):
|
||||||
|
for call in calls:
|
||||||
|
if isinstance(call, Array) and call.type != Array.DICT:
|
||||||
|
for stmt in call:
|
||||||
|
search_calls(stmt.expression_list())
|
||||||
|
elif isinstance(call, Call):
|
||||||
|
c = call
|
||||||
|
# Check if there's an execution in it, if so this is
|
||||||
|
# not a set_var.
|
||||||
|
while True:
|
||||||
|
if c.next is None or isinstance(c.next, Array):
|
||||||
|
break
|
||||||
|
c = c.next
|
||||||
|
dct[unicode(c.name)].append(call)
|
||||||
|
|
||||||
|
for calls, operation in self.assignment_details:
|
||||||
|
search_calls(calls)
|
||||||
|
|
||||||
|
if not self.assignment_details and self._names_are_set_vars:
|
||||||
|
# In the case of Param, it's also a defining name without ``=``
|
||||||
|
search_calls(self.expression_list())
|
||||||
|
|
||||||
|
for as_name in self.as_names:
|
||||||
|
dct[unicode(as_name)].append(Call(self._sub_module, as_name,
|
||||||
|
as_name.start_pos, as_name.end_pos, self))
|
||||||
|
return dct
|
||||||
|
|
||||||
def is_global(self):
|
def is_global(self):
|
||||||
p = self.parent
|
p = self.parent
|
||||||
return isinstance(p, KeywordStatement) and p.name == 'global'
|
return isinstance(p, KeywordStatement) and p.name == 'global'
|
||||||
@@ -1050,7 +1095,7 @@ class Statement(Simple, DocstringMixin):
|
|||||||
return arr, break_tok
|
return arr, break_tok
|
||||||
|
|
||||||
def parse_stmt(token_iterator, maybe_dict=False, added_breaks=(),
|
def parse_stmt(token_iterator, maybe_dict=False, added_breaks=(),
|
||||||
break_on_assignment=False, stmt_class=Statement,
|
break_on_assignment=False, stmt_class=ArrayStmt,
|
||||||
allow_comma=False):
|
allow_comma=False):
|
||||||
token_list = []
|
token_list = []
|
||||||
level = 0
|
level = 0
|
||||||
@@ -1064,7 +1109,7 @@ class Statement(Simple, DocstringMixin):
|
|||||||
first = False
|
first = False
|
||||||
|
|
||||||
if isinstance(tok, Base):
|
if isinstance(tok, Base):
|
||||||
# the token is a Name, which has already been parsed
|
# The token is a Name, which has already been parsed.
|
||||||
if not level:
|
if not level:
|
||||||
if isinstance(tok, ListComprehension):
|
if isinstance(tok, ListComprehension):
|
||||||
# it's not possible to set it earlier
|
# it's not possible to set it earlier
|
||||||
@@ -1134,13 +1179,10 @@ class Statement(Simple, DocstringMixin):
|
|||||||
added_breaks=added_breaks)
|
added_breaks=added_breaks)
|
||||||
|
|
||||||
if stmt is not None:
|
if stmt is not None:
|
||||||
for t in stmt._token_list:
|
|
||||||
if isinstance(t, Name):
|
|
||||||
t.parent = stmt
|
|
||||||
stmt._names_are_set_vars = names_are_set_vars
|
stmt._names_are_set_vars = names_are_set_vars
|
||||||
return stmt, tok
|
return stmt, tok
|
||||||
|
|
||||||
st = Statement(self._sub_module, token_list, start_pos,
|
st = ArrayStmt(self._sub_module, token_list, start_pos,
|
||||||
end_pos, set_name_parents=False)
|
end_pos, set_name_parents=False)
|
||||||
|
|
||||||
middle, tok = parse_stmt_or_arr(token_iterator, ['in'], True)
|
middle, tok = parse_stmt_or_arr(token_iterator, ['in'], True)
|
||||||
@@ -1170,7 +1212,7 @@ class Statement(Simple, DocstringMixin):
|
|||||||
next(token_iterator, None)
|
next(token_iterator, None)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# the token is a Name, which has already been parsed
|
# The token is a Name, which has already been parsed
|
||||||
tok_str = tok
|
tok_str = tok
|
||||||
token_type = None
|
token_type = None
|
||||||
|
|
||||||
@@ -1190,7 +1232,7 @@ class Statement(Simple, DocstringMixin):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
is_literal = token_type in (tokenize.STRING, tokenize.NUMBER)
|
is_literal = token_type in (tokenize.STRING, tokenize.NUMBER)
|
||||||
if isinstance(tok_str, Name) or is_literal:
|
if is_literal or isinstance(tok, Name):
|
||||||
cls = Literal if is_literal else Call
|
cls = Literal if is_literal else Call
|
||||||
|
|
||||||
call = cls(self._sub_module, tok_str, tok.start_pos, tok.end_pos, self)
|
call = cls(self._sub_module, tok_str, tok.start_pos, tok.end_pos, self)
|
||||||
@@ -1213,7 +1255,7 @@ class Statement(Simple, DocstringMixin):
|
|||||||
is_chain = True
|
is_chain = True
|
||||||
elif tok_str == ',' and result: # implies a tuple
|
elif tok_str == ',' and result: # implies a tuple
|
||||||
# expression is now an array not a statement anymore
|
# expression is now an array not a statement anymore
|
||||||
stmt = Statement(self._sub_module, result, result[0].start_pos,
|
stmt = ArrayStmt(self._sub_module, result, result[0].start_pos,
|
||||||
tok.end_pos, self.parent, set_name_parents=False)
|
tok.end_pos, self.parent, set_name_parents=False)
|
||||||
stmt._expression_list = result
|
stmt._expression_list = result
|
||||||
arr, break_tok = parse_array(token_iterator, Array.TUPLE,
|
arr, break_tok = parse_array(token_iterator, Array.TUPLE,
|
||||||
@@ -1247,6 +1289,14 @@ class ExprStmt(Statement):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ArrayStmt(Statement):
|
||||||
|
"""
|
||||||
|
This class exists temporarily. Like ``ExprStatement``, this exists to
|
||||||
|
distinguish between real statements and stuff that is defined in those
|
||||||
|
statements.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Param(ExprStmt):
|
class Param(ExprStmt):
|
||||||
"""
|
"""
|
||||||
The class which shows definitions of params of classes and functions.
|
The class which shows definitions of params of classes and functions.
|
||||||
@@ -1308,8 +1358,7 @@ class StatementElement(Simple):
|
|||||||
def generate_call_path(self):
|
def generate_call_path(self):
|
||||||
""" Helps to get the order in which statements are executed. """
|
""" Helps to get the order in which statements are executed. """
|
||||||
try:
|
try:
|
||||||
for name_part in self.name.names:
|
yield self.name
|
||||||
yield name_part
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
yield self
|
yield self
|
||||||
if self.next is not None:
|
if self.next is not None:
|
||||||
@@ -1318,7 +1367,7 @@ class StatementElement(Simple):
|
|||||||
|
|
||||||
def get_code(self):
|
def get_code(self):
|
||||||
if self.next is not None:
|
if self.next is not None:
|
||||||
s = '.' if isinstance(self, Array) else ''
|
s = '.' if not isinstance(self.next, Array) else ''
|
||||||
return s + self.next.get_code()
|
return s + self.next.get_code()
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@@ -1334,6 +1383,21 @@ class Call(StatementElement):
|
|||||||
def get_code(self):
|
def get_code(self):
|
||||||
return self.name.get_code() + super(Call, self).get_code()
|
return self.name.get_code() + super(Call, self).get_code()
|
||||||
|
|
||||||
|
def names(self):
|
||||||
|
"""
|
||||||
|
Generate an array of string names. If a call is not just names,
|
||||||
|
raise an error.
|
||||||
|
"""
|
||||||
|
def check(call):
|
||||||
|
while call is not None:
|
||||||
|
if not isinstance(call, Call): # Could be an Array.
|
||||||
|
break
|
||||||
|
yield unicode(call.name)
|
||||||
|
call = call.next
|
||||||
|
|
||||||
|
return list(check(self))
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: %s>" % (type(self).__name__, self.name)
|
return "<%s: %s>" % (type(self).__name__, self.name)
|
||||||
|
|
||||||
@@ -1450,7 +1514,7 @@ class Array(StatementElement):
|
|||||||
return "<%s: %s%s>" % (type(self).__name__, typ, self.values)
|
return "<%s: %s%s>" % (type(self).__name__, typ, self.values)
|
||||||
|
|
||||||
|
|
||||||
class NamePart(object):
|
class Name(object):
|
||||||
"""
|
"""
|
||||||
A string. Sometimes it is important to know if the string belongs to a name
|
A string. Sometimes it is important to know if the string belongs to a name
|
||||||
or not.
|
or not.
|
||||||
@@ -1474,13 +1538,14 @@ class NamePart(object):
|
|||||||
return self._string
|
return self._string
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: %s>" % (type(self).__name__, self._string)
|
return "<%s: %s@%s,%s>" % (type(self).__name__, self._string,
|
||||||
|
self.start_pos[0], self.start_pos[1])
|
||||||
|
|
||||||
def get_code(self):
|
def get_code(self):
|
||||||
return self._string
|
return self._string
|
||||||
|
|
||||||
def get_definition(self):
|
def get_definition(self):
|
||||||
return self.parent.get_definition()
|
return self.get_parent_until((ArrayStmt, StatementElement), reverse=True)
|
||||||
|
|
||||||
def get_parent_until(self, *args, **kwargs):
|
def get_parent_until(self, *args, **kwargs):
|
||||||
return self.parent.get_parent_until(*args, **kwargs)
|
return self.parent.get_parent_until(*args, **kwargs)
|
||||||
@@ -1498,48 +1563,6 @@ class NamePart(object):
|
|||||||
return self.start_pos[0], self.start_pos[1] + len(self._string)
|
return self.start_pos[0], self.start_pos[1] + len(self._string)
|
||||||
|
|
||||||
|
|
||||||
class Name(Simple):
|
|
||||||
"""
|
|
||||||
Used to define names in python.
|
|
||||||
Which means the whole namespace/class/function stuff.
|
|
||||||
So a name like "module.class.function"
|
|
||||||
would result in an array of [module, class, function]
|
|
||||||
"""
|
|
||||||
__slots__ = ('names', '_get_code')
|
|
||||||
|
|
||||||
def __init__(self, module, names, start_pos, end_pos, parent=None):
|
|
||||||
super(Name, self).__init__(module, start_pos, end_pos, parent)
|
|
||||||
# Cache get_code, because it's used quite often for comparisons
|
|
||||||
# (seen by using the profiler).
|
|
||||||
self._get_code = ".".join(n[0] for n in names)
|
|
||||||
|
|
||||||
names = tuple(NamePart(module, n[0], self, n[1]) for n in names)
|
|
||||||
self.names = names
|
|
||||||
|
|
||||||
def get_code(self):
|
|
||||||
""" Returns the names in a full string format """
|
|
||||||
return self._get_code
|
|
||||||
|
|
||||||
def get_definition(self):
|
|
||||||
# TODO This is way to complicated, simplify this with a new parser.
|
|
||||||
return self.get_parent_until((ExprStmt, IsScope, Import))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def end_pos(self):
|
|
||||||
return self.names[-1].end_pos
|
|
||||||
|
|
||||||
@property
|
|
||||||
def docstr(self):
|
|
||||||
"""Return attribute docstring (PEP 257) if exists."""
|
|
||||||
return self.parent.docstr
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.get_code()
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.names)
|
|
||||||
|
|
||||||
|
|
||||||
class ListComprehension(ForFlow):
|
class ListComprehension(ForFlow):
|
||||||
""" Helper class for list comprehensions """
|
""" Helper class for list comprehensions """
|
||||||
def __init__(self, module, stmt, middle, input, parent):
|
def __init__(self, module, stmt, middle, input, parent):
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
This is used for dynamic object completion.
|
This is used for dynamic object completion.
|
||||||
Jedi tries to guess the types with a backtracking approach.
|
Jedi tries to guess the types with a backtracking approach.
|
||||||
"""
|
"""
|
||||||
def func(a):
|
def func(a, default_arg=2):
|
||||||
|
#? int()
|
||||||
|
default_arg
|
||||||
#? int() str()
|
#? int() str()
|
||||||
return a
|
return a
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ class TestClass(Super):
|
|||||||
TestClass.base_var
|
TestClass.base_var
|
||||||
|
|
||||||
|
|
||||||
#< 13 (5,13), (0,13)
|
#< 13 (5,13), (0,13), (-24,13)
|
||||||
self.instance_var = 3
|
self.instance_var = 3
|
||||||
|
|
||||||
#< 9 (0,8),
|
#< 9 (0,8),
|
||||||
|
|||||||
@@ -148,6 +148,28 @@ def test_signature_params():
|
|||||||
check(Script(s + '\nbar=foo\nbar').goto_assignments())
|
check(Script(s + '\nbar=foo\nbar').goto_assignments())
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsDefinition(TestCase):
|
||||||
|
def _def(self, source, index=-1):
|
||||||
|
return names(dedent(source), references=True, all_scopes=True)[index]
|
||||||
|
|
||||||
|
def test_name(self):
|
||||||
|
d = self._def('name')
|
||||||
|
assert d.name == 'name'
|
||||||
|
assert not d.is_definition()
|
||||||
|
|
||||||
|
def test_stmt(self):
|
||||||
|
src = 'a = f(x)'
|
||||||
|
d = self._def(src, 0)
|
||||||
|
assert d.name == 'a'
|
||||||
|
assert d.is_definition()
|
||||||
|
d = self._def(src, 1)
|
||||||
|
assert d.name == 'f'
|
||||||
|
assert not d.is_definition()
|
||||||
|
d = self._def(src)
|
||||||
|
assert d.name == 'x'
|
||||||
|
assert not d.is_definition()
|
||||||
|
|
||||||
|
|
||||||
class TestParent(TestCase):
|
class TestParent(TestCase):
|
||||||
def _parent(self, source, line=None, column=None):
|
def _parent(self, source, line=None, column=None):
|
||||||
defs = Script(dedent(source), line, column).goto_assignments()
|
defs = Script(dedent(source), line, column).goto_assignments()
|
||||||
@@ -219,3 +241,60 @@ class TestGotoAssignments(TestCase):
|
|||||||
param = bar.goto_assignments()[0]
|
param = bar.goto_assignments()[0]
|
||||||
assert param.start_pos == (1, 13)
|
assert param.start_pos == (1, 13)
|
||||||
assert param.type == 'param'
|
assert param.type == 'param'
|
||||||
|
|
||||||
|
def test_class_call(self):
|
||||||
|
src = 'from threading import Thread; Thread(group=1)'
|
||||||
|
n = names(src, references=True)[-1]
|
||||||
|
assert n.name == 'group'
|
||||||
|
param_def = n.goto_assignments()[0]
|
||||||
|
assert param_def.name == 'group'
|
||||||
|
assert param_def.type == 'param'
|
||||||
|
|
||||||
|
def test_parentheses(self):
|
||||||
|
n = names('("").upper', references=True)[-1]
|
||||||
|
assert n.goto_assignments()[0].name == 'upper'
|
||||||
|
|
||||||
|
def test_import(self):
|
||||||
|
nms = names('from json import load', references=True)
|
||||||
|
assert nms[0].name == 'json'
|
||||||
|
assert nms[0].type == 'import'
|
||||||
|
n = nms[0].goto_assignments()[0]
|
||||||
|
assert n.name == 'json'
|
||||||
|
assert n.type == 'module'
|
||||||
|
|
||||||
|
assert nms[1].name == 'load'
|
||||||
|
assert nms[1].type == 'import'
|
||||||
|
n = nms[1].goto_assignments()[0]
|
||||||
|
assert n.name == 'load'
|
||||||
|
assert n.type == 'function'
|
||||||
|
|
||||||
|
nms = names('import os; os.path', references=True)
|
||||||
|
assert nms[0].name == 'os'
|
||||||
|
assert nms[0].type == 'import'
|
||||||
|
n = nms[0].goto_assignments()[0]
|
||||||
|
assert n.name == 'os'
|
||||||
|
assert n.type == 'module'
|
||||||
|
|
||||||
|
n = nms[2].goto_assignments()[0]
|
||||||
|
assert n.name == 'path'
|
||||||
|
assert n.type == 'import'
|
||||||
|
|
||||||
|
nms = names('import os.path', references=True)
|
||||||
|
n = nms[0].goto_assignments()[0]
|
||||||
|
assert n.name == 'os'
|
||||||
|
assert n.type == 'module'
|
||||||
|
n = nms[1].goto_assignments()[0]
|
||||||
|
assert n.name == 'path'
|
||||||
|
assert n.type == 'import'
|
||||||
|
|
||||||
|
def test_import_alias(self):
|
||||||
|
nms = names('import json as foo', references=True)
|
||||||
|
assert nms[0].name == 'json'
|
||||||
|
assert nms[0].type == 'import'
|
||||||
|
n = nms[0].goto_assignments()[0]
|
||||||
|
assert n.name == 'json'
|
||||||
|
assert n.type == 'module'
|
||||||
|
|
||||||
|
assert nms[1].name == 'foo'
|
||||||
|
assert nms[1].type == 'import'
|
||||||
|
assert [nms[1]] == nms[1].goto_assignments()
|
||||||
|
|||||||
@@ -1,18 +1,9 @@
|
|||||||
from jedi._compatibility import unicode
|
from jedi._compatibility import unicode
|
||||||
|
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.parser import representation as pr
|
|
||||||
from jedi.parser import Parser
|
from jedi.parser import Parser
|
||||||
|
|
||||||
|
|
||||||
def test_deep_ast_copy():
|
|
||||||
name = pr.Name(object, [('hallo', (0, 0))], (0, 0), (0, 0))
|
|
||||||
|
|
||||||
# fast parent copy should switch parent
|
|
||||||
new_name = helpers.deep_ast_copy(name)
|
|
||||||
assert new_name.names[0].parent == new_name
|
|
||||||
|
|
||||||
|
|
||||||
def test_statement_elements_in_statement():
|
def test_statement_elements_in_statement():
|
||||||
def get_stmt_els(string):
|
def get_stmt_els(string):
|
||||||
p = Parser(unicode(string))
|
p = Parser(unicode(string))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from jedi import Script
|
|||||||
|
|
||||||
def get_definition_and_evaluator(source):
|
def get_definition_and_evaluator(source):
|
||||||
d = Script(dedent(source)).goto_definitions()[0]
|
d = Script(dedent(source)).goto_definitions()[0]
|
||||||
return d._name.parent.parent, d._evaluator
|
return d._name.parent, d._evaluator
|
||||||
|
|
||||||
|
|
||||||
def test_function_execution():
|
def test_function_execution():
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ asdfasdf""" + "h"
|
|||||||
def test_tokenizer_with_string_literal_backslash():
|
def test_tokenizer_with_string_literal_backslash():
|
||||||
import jedi
|
import jedi
|
||||||
c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions()
|
c = jedi.Script("statement = u'foo\\\n'; statement").goto_definitions()
|
||||||
assert c[0]._name.parent.parent.obj == 'foo'
|
assert c[0]._name.parent.obj == 'foo'
|
||||||
|
|||||||
Reference in New Issue
Block a user