forked from VimPlug/jedi
Merge remote-tracking branch 'upstream/dev' into fix_runtime_error
Conflicts: jedi/evaluate/imports.py
This commit is contained in:
+41
-23
@@ -69,7 +69,7 @@ backtracking algorithm.
|
||||
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
|
||||
"""
|
||||
import copy
|
||||
import itertools
|
||||
from itertools import tee, chain
|
||||
|
||||
from jedi._compatibility import next, hasattr, unicode
|
||||
from jedi.parser import representation as pr
|
||||
@@ -172,20 +172,7 @@ class Evaluator(object):
|
||||
return precedence.process_precedence_element(self, p) or []
|
||||
|
||||
def eval_statement_element(self, element):
|
||||
if pr.Array.is_type(element, pr.Array.NOARRAY):
|
||||
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):
|
||||
if isinstance(element, pr.ListComprehension):
|
||||
return self.eval_statement(element.stmt)
|
||||
elif isinstance(element, pr.Lambda):
|
||||
return [er.Function(self, element)]
|
||||
@@ -219,9 +206,20 @@ class Evaluator(object):
|
||||
current = next(path)
|
||||
|
||||
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:
|
||||
if isinstance(current, pr.NamePart):
|
||||
if isinstance(current, pr.Name):
|
||||
# This is the first global lookup.
|
||||
types = self.find_types(scope, current, position=position,
|
||||
search_global=True)
|
||||
@@ -241,7 +239,7 @@ class Evaluator(object):
|
||||
to follow a call like ``module.a_type.Foo.bar`` (in ``from_somewhere``).
|
||||
"""
|
||||
results_new = []
|
||||
iter_paths = itertools.tee(path, len(types))
|
||||
iter_paths = tee(path, len(types))
|
||||
|
||||
for i, typ in enumerate(types):
|
||||
fp = self._follow_path(iter_paths[i], typ, call_scope)
|
||||
@@ -320,12 +318,26 @@ class Evaluator(object):
|
||||
return types
|
||||
|
||||
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
|
||||
# statement name definitions. Only return, if it's one name and one
|
||||
# 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.
|
||||
if len(call_path) == 1 and isinstance(call_path[0], pr.NamePart) \
|
||||
and call_path[0] in [d.names[-1] for d in stmt.get_defined_names()]:
|
||||
if len(call_path) == 1 and isinstance(call_path[0], pr.Name) \
|
||||
and call_path[0] in stmt.get_defined_names():
|
||||
# Named params should get resolved to their param definitions.
|
||||
if pr.Array.is_type(stmt.parent, pr.Array.TUPLE, pr.Array.NOARRAY) \
|
||||
and stmt.parent.previous:
|
||||
@@ -337,9 +349,15 @@ class Evaluator(object):
|
||||
param_names = []
|
||||
named_param_name = stmt.get_defined_names()[0]
|
||||
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):
|
||||
param_names.append(param.get_name().names[-1])
|
||||
param_names.append(param.get_name())
|
||||
return param_names
|
||||
return [call_path[0]]
|
||||
|
||||
@@ -364,7 +382,7 @@ class Evaluator(object):
|
||||
|
||||
def filter_private_variable(scope, call_scope, var_name):
|
||||
"""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)\
|
||||
and var_name.startswith('__') and not var_name.endswith('__'):
|
||||
s = call_scope.get_parent_until((pr.Class, er.Instance, compiled.CompiledObject))
|
||||
|
||||
@@ -240,7 +240,6 @@ class CompiledName(FakeName):
|
||||
super(CompiledName, self).__init__(name)
|
||||
self._obj = obj
|
||||
self.name = name
|
||||
self.start_pos = 0, 0 # an illegal start_pos, to make sorting easy.
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
|
||||
@@ -87,7 +87,7 @@ def search_params(evaluator, param):
|
||||
|
||||
# Need to take right index, because there could be a
|
||||
# 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]
|
||||
i = listRightIndex(call_path_simple, func_name)
|
||||
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 p in params:
|
||||
if str(p) == param_name:
|
||||
result += evaluator.eval_statement(p.parent)
|
||||
result += evaluator.eval_statement(p.get_definition())
|
||||
return result
|
||||
|
||||
func = param.get_parent_until(pr.Function)
|
||||
|
||||
+19
-15
@@ -42,7 +42,7 @@ class NameFinder(object):
|
||||
types = self._names_to_types(names, resolve_decorator)
|
||||
|
||||
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)):
|
||||
if not isinstance(self.name_str, (str, unicode)): # TODO Remove?
|
||||
if search_global:
|
||||
@@ -102,18 +102,18 @@ class NameFinder(object):
|
||||
or isinstance(scope, compiled.CompiledObject) \
|
||||
or isinstance(stmt, pr.ExprStmt) and stmt.is_global():
|
||||
# Always reachable.
|
||||
names.append(name.names[-1])
|
||||
names.append(name)
|
||||
else:
|
||||
check = flow_analysis.break_check(self._evaluator,
|
||||
name_list_scope,
|
||||
er.wrap(self._evaluator, scope),
|
||||
self.scope)
|
||||
if check is not flow_analysis.UNREACHABLE:
|
||||
names.append(name.names[-1])
|
||||
names.append(name)
|
||||
if check is flow_analysis.REACHABLE:
|
||||
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):
|
||||
break
|
||||
else:
|
||||
@@ -139,16 +139,16 @@ class NameFinder(object):
|
||||
evaluation, so remove them already here!
|
||||
"""
|
||||
for n in names:
|
||||
definition = n.parent.parent
|
||||
definition = n.parent
|
||||
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:
|
||||
yield n
|
||||
|
||||
def _check_getattr(self, inst):
|
||||
"""Checks for both __getattr__ and __getattribute__ methods"""
|
||||
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))
|
||||
with common.ignored(KeyError):
|
||||
result = inst.execute_subscope_by_name('__getattr__', [name])
|
||||
@@ -161,12 +161,12 @@ class NameFinder(object):
|
||||
result = inst.execute_subscope_by_name('__getattribute__', [name])
|
||||
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.
|
||||
"""
|
||||
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
|
||||
elif isinstance(stmt, pr.Import) and stmt.is_nested():
|
||||
return False
|
||||
@@ -219,7 +219,7 @@ class NameFinder(object):
|
||||
evaluator = self._evaluator
|
||||
|
||||
# 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
|
||||
# Ignore FunctionExecution parents for now.
|
||||
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:
|
||||
as_names = p.inputs[0].as_names
|
||||
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
|
||||
# the static analysis report.
|
||||
types = list(chain.from_iterable(
|
||||
@@ -395,7 +395,7 @@ def check_flow_information(evaluator, flow, search_name_part, pos):
|
||||
return result
|
||||
|
||||
|
||||
def _check_isinstance_type(evaluator, stmt, search_name_part):
|
||||
def _check_isinstance_type(evaluator, stmt, search_name):
|
||||
try:
|
||||
expression_list = stmt.expression_list()
|
||||
# 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 isinstance(obj[0], pr.Call)
|
||||
|
||||
# names fit?
|
||||
assert unicode(obj[0].name) == unicode(search_name_part.parent)
|
||||
prev = search_name.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
|
||||
except AssertionError:
|
||||
return []
|
||||
@@ -583,7 +587,7 @@ def find_assignments(lhs, results, seek_name):
|
||||
"""
|
||||
if isinstance(lhs, pr.Array):
|
||||
return _assign_tuples(lhs, results, seek_name)
|
||||
elif unicode(lhs.name.names[-1]) == seek_name:
|
||||
elif unicode(lhs.name) == seek_name:
|
||||
return results
|
||||
else:
|
||||
return []
|
||||
|
||||
+18
-17
@@ -1,6 +1,7 @@
|
||||
import copy
|
||||
from itertools import chain
|
||||
|
||||
from jedi._compatibility import unicode
|
||||
from jedi.parser import representation as pr
|
||||
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')
|
||||
|
||||
new_elements = new_elements_default or {}
|
||||
accept = (pr.Simple, pr.NamePart, pr.KeywordStatement)
|
||||
accept = (pr.Simple, pr.Name, pr.KeywordStatement)
|
||||
|
||||
def recursion(obj):
|
||||
# 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
|
||||
# tree in there.
|
||||
items = sorted(items, key=sort_stmt)
|
||||
else:
|
||||
items = sorted(items, key=lambda x: x[0] == '_names_dict')
|
||||
|
||||
# Actually copy and set attributes.
|
||||
new_obj = copy.copy(obj)
|
||||
@@ -67,13 +70,17 @@ def deep_ast_copy(obj, new_elements_default=None):
|
||||
pass
|
||||
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
|
||||
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)):
|
||||
setattr(new_obj, key, list_or_tuple_rec(value))
|
||||
setattr(new_obj, key, sequence_recursion(value))
|
||||
elif isinstance(value, accept):
|
||||
setattr(new_obj, key, recursion(value))
|
||||
|
||||
return new_obj
|
||||
|
||||
def list_or_tuple_rec(array_obj):
|
||||
def sequence_recursion(array_obj):
|
||||
if isinstance(array_obj, tuple):
|
||||
copied_array = list(array_obj)
|
||||
else:
|
||||
@@ -82,7 +89,7 @@ def deep_ast_copy(obj, new_elements_default=None):
|
||||
if isinstance(el, accept):
|
||||
copied_array[i] = recursion(el)
|
||||
elif isinstance(el, (tuple, list)):
|
||||
copied_array[i] = list_or_tuple_rec(el)
|
||||
copied_array[i] = sequence_recursion(el)
|
||||
|
||||
if isinstance(array_obj, tuple):
|
||||
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):
|
||||
result += scan_array(s_new, search_name)
|
||||
else:
|
||||
n = s_new.name
|
||||
if isinstance(n, pr.Name) \
|
||||
and search_name in [str(x) for x in n.names]:
|
||||
if search_name == unicode(s_new.name):
|
||||
result.append(c)
|
||||
|
||||
s_new = s_new.next
|
||||
@@ -217,7 +222,7 @@ def get_module_name_parts(module):
|
||||
def scope_name_parts(scope):
|
||||
for s in scope.subscopes:
|
||||
# Yield the name parts, not names.
|
||||
yield s.name.names[0]
|
||||
yield s.name
|
||||
for need_yield_from in scope_name_parts(s):
|
||||
yield need_yield_from
|
||||
|
||||
@@ -226,7 +231,7 @@ def get_module_name_parts(module):
|
||||
for stmt_or_import in statements_or_imports:
|
||||
if isinstance(stmt_or_import, pr.Import):
|
||||
for name in stmt_or_import.get_all_import_names():
|
||||
name_parts.update(name.names)
|
||||
name_parts.add(name)
|
||||
else:
|
||||
# Running this ensures that all the expression lists are generated
|
||||
# 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.
|
||||
for tok in stmt_or_import._token_list:
|
||||
if isinstance(tok, pr.Name):
|
||||
name_parts.update(tok.names)
|
||||
name_parts.add(tok)
|
||||
|
||||
return name_parts
|
||||
|
||||
@@ -298,18 +303,14 @@ class FakeStatement(pr.ExprStmt):
|
||||
class FakeImport(pr.Import):
|
||||
def __init__(self, name, parent, level=0):
|
||||
p = 0, 0
|
||||
super(FakeImport, self).__init__(FakeSubModule, p, p, name,
|
||||
super(FakeImport, self).__init__(FakeSubModule, p, p, [name],
|
||||
relative_count=level)
|
||||
self.parent = parent
|
||||
|
||||
|
||||
class FakeName(pr.Name):
|
||||
def __init__(self, name_or_names, parent=None, start_pos=(0, 0)):
|
||||
if isinstance(name_or_names, list):
|
||||
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 __init__(self, name_str, parent=None, start_pos=(0, 0)):
|
||||
super(FakeName, self).__init__(FakeSubModule, name_str, parent, start_pos)
|
||||
|
||||
def get_definition(self):
|
||||
return self.parent
|
||||
|
||||
+70
-65
@@ -67,13 +67,13 @@ class ImportWrapper(pr.Base):
|
||||
|
||||
# rest is import_path resolution
|
||||
import_path = []
|
||||
if import_stmt.from_ns:
|
||||
import_path += import_stmt.from_ns.names
|
||||
if import_stmt.namespace:
|
||||
if import_stmt.from_names:
|
||||
import_path += import_stmt.from_names
|
||||
if import_stmt.namespace_names:
|
||||
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:
|
||||
import_path += import_stmt.namespace.names
|
||||
import_path += import_stmt.namespace_names
|
||||
|
||||
for i in range(kill_count + int(is_like_search)):
|
||||
if import_path:
|
||||
@@ -110,6 +110,7 @@ class ImportWrapper(pr.Base):
|
||||
m = _load_module(rel_path)
|
||||
names += m.get_defined_names()
|
||||
else:
|
||||
# flask
|
||||
if self.import_path == ('flask', 'ext'):
|
||||
# List Flask extensions like ``flask_foo``
|
||||
for mod in self._get_module_names():
|
||||
@@ -122,6 +123,8 @@ class ImportWrapper(pr.Base):
|
||||
flaskext = os.path.join(dir, 'flaskext')
|
||||
if os.path.isdir(flaskext):
|
||||
names += self._get_module_names([flaskext])
|
||||
|
||||
# namespace packages
|
||||
if on_import_stmt and isinstance(scope, pr.Module) \
|
||||
and scope.path.endswith('__init__.py'):
|
||||
pkg_path = os.path.dirname(scope.path)
|
||||
@@ -136,18 +139,18 @@ class ImportWrapper(pr.Base):
|
||||
# ``sys.modules`` modification.
|
||||
names.append(self._generate_name('path'))
|
||||
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
|
||||
for s, scope_names in finder.get_names_of_scope(self._evaluator,
|
||||
scope, include_builtin=False):
|
||||
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)
|
||||
return names
|
||||
|
||||
@@ -179,55 +182,57 @@ class ImportWrapper(pr.Base):
|
||||
# check recursion
|
||||
return []
|
||||
|
||||
if self.import_path:
|
||||
try:
|
||||
module, rest = self._importer.follow_file_system()
|
||||
except ModuleNotFound as e:
|
||||
analysis.add(self._evaluator, 'import-error', e.name_part)
|
||||
return []
|
||||
try:
|
||||
if self.import_path:
|
||||
try:
|
||||
module, rest = self._importer.follow_file_system()
|
||||
except ModuleNotFound as e:
|
||||
analysis.add(self._evaluator, 'import-error', e.name_part)
|
||||
return []
|
||||
|
||||
if module is None:
|
||||
return []
|
||||
if module is None:
|
||||
return []
|
||||
|
||||
if self.import_stmt.is_nested() and not self.nested_resolve:
|
||||
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))
|
||||
if self.import_stmt.is_nested() and not self.nested_resolve:
|
||||
scopes = [NestedImportModule(module, self.import_stmt)]
|
||||
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])
|
||||
self._evaluator.recursion_detector.pop_stmt()
|
||||
scopes = [module]
|
||||
|
||||
star_imports = remove_star_imports(self._evaluator, module)
|
||||
if star_imports:
|
||||
scopes = [StarImportModule(scopes[0], star_imports)]
|
||||
|
||||
# goto only accepts `Name`
|
||||
if is_goto and not rest:
|
||||
scopes = [s.name 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:
|
||||
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
|
||||
|
||||
|
||||
@@ -244,12 +249,12 @@ class NestedImportModule(pr.Module):
|
||||
# This is not an existing Import statement. Therefore, set position to
|
||||
# 0 (0 is not a valid line number).
|
||||
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)
|
||||
new = pr.Import(i._sub_module, zero, zero, name)
|
||||
new.parent = self._module
|
||||
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):
|
||||
"""
|
||||
@@ -330,7 +335,7 @@ class _Importer(object):
|
||||
self.file_path = os.path.dirname(path) if path is not None else None
|
||||
|
||||
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)
|
||||
|
||||
def get_relative_path(self):
|
||||
@@ -372,12 +377,12 @@ class _Importer(object):
|
||||
pos = (part._line, part._column)
|
||||
try:
|
||||
self.import_path = (
|
||||
pr.NamePart(FakeSubModule, 'flask_' + str(part), part.parent, pos),
|
||||
pr.Name(FakeSubModule, 'flask_' + str(part), part.parent, pos),
|
||||
) + orig_path[3:]
|
||||
return self._real_follow_file_system()
|
||||
except ModuleNotFound as e:
|
||||
self.import_path = (
|
||||
pr.NamePart(FakeSubModule, 'flaskext', part.parent, pos),
|
||||
pr.Name(FakeSubModule, 'flaskext', part.parent, pos),
|
||||
) + orig_path[2:]
|
||||
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):
|
||||
# make testing easier, sort it - same results on every interpreter
|
||||
c = check_python_file(p)
|
||||
if c is not None and c not in mods:
|
||||
if c is not None and c not in mods and not isinstance(c, compiled.CompiledObject):
|
||||
yield c
|
||||
|
||||
@@ -193,14 +193,14 @@ class Array(use_metaclass(CachedMetaClass, IterableWrapper)):
|
||||
|
||||
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, ...
|
||||
"""
|
||||
# `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.execute(scope)[0] # builtins only have one class
|
||||
from jedi.evaluate.representation import get_instance_el
|
||||
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
|
||||
def parent(self):
|
||||
@@ -225,34 +225,6 @@ class Array(use_metaclass(CachedMetaClass, IterableWrapper)):
|
||||
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):
|
||||
def __init__(self, evaluator, arrays):
|
||||
super(MergedArray, self).__init__(evaluator, arrays[-1]._array)
|
||||
@@ -342,7 +314,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
|
||||
result = []
|
||||
for c in calls:
|
||||
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]
|
||||
separate_index = call_path_simple.index(add_name)
|
||||
if add_name == call_path_simple[-1] or separate_index == 0:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import copy
|
||||
|
||||
from jedi._compatibility import unicode, zip_longest
|
||||
from jedi import debug
|
||||
from jedi import common
|
||||
from jedi.parser import representation as pr
|
||||
from jedi.evaluate import iterable
|
||||
from jedi import common
|
||||
from jedi.evaluate import helpers
|
||||
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():
|
||||
yield helpers.FakeStatement([field_stmt])
|
||||
elif isinstance(array, Instance) and array.name.get_code() == 'tuple':
|
||||
pass
|
||||
debug.warning('Ignored a tuple *args input %s' % array)
|
||||
else:
|
||||
if expression_list:
|
||||
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):
|
||||
key = call.name
|
||||
else:
|
||||
debug.warning('Ignored complicated **kwargs stmt %s' % call)
|
||||
continue # We ignore complicated statements here, for now.
|
||||
|
||||
# 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])
|
||||
|
||||
name = copy.copy(param.get_name())
|
||||
name.names = [copy.copy(name.names[0])]
|
||||
name.names[0].parent = name
|
||||
name.parent = new_param
|
||||
return name
|
||||
|
||||
|
||||
@@ -164,12 +164,14 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
|
||||
# because to follow them and their self variables is too
|
||||
# complicated.
|
||||
sub = self._get_method_execution(sub)
|
||||
for n in sub.get_defined_names():
|
||||
# Only names with the selfname are being added.
|
||||
# It is also important, that they have a len() of 2,
|
||||
# because otherwise, they are just something else
|
||||
if unicode(n.names[0]) == self_name and len(n.names) == 2:
|
||||
add_self_dot_name(n)
|
||||
for per_name_list in sub.get_names_dict().values():
|
||||
for call in per_name_list:
|
||||
if unicode(call.name) == self_name \
|
||||
and isinstance(call.next, pr.Call) \
|
||||
and call.next.next is None:
|
||||
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):
|
||||
if not isinstance(s, compiled.CompiledObject):
|
||||
@@ -243,7 +245,12 @@ def get_instance_el(evaluator, instance, var, is_class_var=False):
|
||||
untouched.
|
||||
"""
|
||||
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
|
||||
|
||||
var = wrap(evaluator, var)
|
||||
@@ -275,6 +282,9 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)):
|
||||
return par
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
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
|
||||
@underscore_memoization
|
||||
def name(self):
|
||||
@@ -390,6 +394,9 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)):
|
||||
def py__call__(self, evaluator, 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 in_iterable(name, 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)
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
This class is used to evaluate functions and their returns.
|
||||
@@ -570,6 +592,10 @@ class FunctionExecution(Executed):
|
||||
break
|
||||
return types
|
||||
|
||||
@underscore_memoization
|
||||
def get_names_dict(self):
|
||||
return LazyDict(self.base.get_names_dict(), self._copy_list)
|
||||
|
||||
@memoize_default(default=())
|
||||
def _get_params(self):
|
||||
"""
|
||||
@@ -591,15 +617,13 @@ class FunctionExecution(Executed):
|
||||
names = pr.filter_after_position(pr.Scope.get_defined_names(self), position)
|
||||
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
|
||||
expensive, because it is something like `copy.deepcopy`. However, these
|
||||
copied objects can be used for the executions, as if they were in the
|
||||
execution.
|
||||
"""
|
||||
# Copy all these lists into this local function.
|
||||
lst = getattr(self.base, list_name)
|
||||
objects = []
|
||||
for element in lst:
|
||||
self._scope_copy(element.parent)
|
||||
@@ -622,22 +646,22 @@ class FunctionExecution(Executed):
|
||||
@common.safe_property
|
||||
@memoize_default([])
|
||||
def returns(self):
|
||||
return self._copy_list('returns')
|
||||
return self._copy_list(self.base.returns)
|
||||
|
||||
@common.safe_property
|
||||
@memoize_default([])
|
||||
def asserts(self):
|
||||
return self._copy_list('asserts')
|
||||
return self._copy_list(self.base.asserts)
|
||||
|
||||
@common.safe_property
|
||||
@memoize_default([])
|
||||
def statements(self):
|
||||
return self._copy_list('statements')
|
||||
return self._copy_list(self.base.statements)
|
||||
|
||||
@common.safe_property
|
||||
@memoize_default([])
|
||||
def subscopes(self):
|
||||
return self._copy_list('subscopes')
|
||||
return self._copy_list(self.base.subscopes)
|
||||
|
||||
def 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.
|
||||
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()
|
||||
def _sub_modules(self):
|
||||
"""
|
||||
@@ -683,6 +712,14 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
||||
imp = helpers.FakeImport(name, self, level=1)
|
||||
name.parent = imp
|
||||
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
|
||||
|
||||
def __getattr__(self, name):
|
||||
|
||||
@@ -60,7 +60,7 @@ def _paths_from_assignment(evaluator, statement):
|
||||
for exp_list, operator in statement.assignment_details:
|
||||
if len(exp_list) != 1 or not isinstance(exp_list[0], pr.Call):
|
||||
continue
|
||||
if unicode(exp_list[0].name) != 'sys.path':
|
||||
if exp_list[0].names() != ['sys', 'path']:
|
||||
continue
|
||||
# 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
|
||||
@@ -88,17 +88,15 @@ def _paths_from_insert(module_path, exe):
|
||||
|
||||
def _paths_from_call_expression(module_path, call):
|
||||
""" extract the path from either "sys.path.append" or "sys.path.insert" """
|
||||
if not call.next_is_execution():
|
||||
return
|
||||
names = call.names()
|
||||
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]
|
||||
exe = call.next
|
||||
exe = call.next.next.next
|
||||
path = None
|
||||
if cmd == 'insert' and len(exe) == 2:
|
||||
path = _paths_from_insert(module_path, exe)
|
||||
elif cmd == 'append' and len(exe) == 1:
|
||||
|
||||
Reference in New Issue
Block a user