Implement Import.is_nested method.

This commit is contained in:
Dave Halter
2014-12-11 16:17:07 +01:00
parent 6406bfb3c2
commit 6818d3affa
4 changed files with 35 additions and 28 deletions
+4 -4
View File
@@ -610,11 +610,11 @@ class Script(object):
def _analysis(self): def _analysis(self):
#statements = set(chain(*self._parser.module().used_names.values())) #statements = set(chain(*self._parser.module().used_names.values()))
stmts, imps = analysis.get_module_statements(self._parser.module()) stmts, imp_names = analysis.get_module_statements(self._parser.module())
# Sort the statements so that the results are reproducible. # Sort the statements so that the results are reproducible.
for i in imps: for n in imp_names:
iw = imports.ImportWrapper(self._evaluator, i, iw = imports.ImportWrapper(self._evaluator, n).follow()
nested_resolve=True).follow() i = n.get_definition()
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):
+4 -3
View File
@@ -212,9 +212,10 @@ def get_module_statements(module):
return new return new
stmts = set() stmts = set()
imports = set() import_names = set()
for scope in module.walk(): for scope in module.walk():
imports |= set(scope.imports) for imp in set(scope.imports):
import_names |= set(imp.get_defined_names())
stmts |= add_stmts(scope.statements) stmts |= add_stmts(scope.statements)
stmts |= add_stmts(r for r in scope.returns if r is not None) stmts |= add_stmts(r for r in scope.returns if r is not None)
@@ -224,4 +225,4 @@ def get_module_statements(module):
pass pass
else: else:
stmts |= add_stmts(decorators) stmts |= add_stmts(decorators)
return stmts, imports return stmts, import_names
+16 -11
View File
@@ -32,9 +32,9 @@ from jedi.evaluate.cache import memoize_default, NO_DEFAULT
class ModuleNotFound(Exception): class ModuleNotFound(Exception):
def __init__(self, name_part): def __init__(self, name):
super(ModuleNotFound, self).__init__() super(ModuleNotFound, self).__init__()
self.name_part = name_part self.name = name
def completion_names(evaluator, imp, pos): def completion_names(evaluator, imp, pos):
@@ -101,16 +101,16 @@ class ImportWrapper(pr.Base):
try: try:
module, rest = importer.follow_file_system() module, rest = importer.follow_file_system()
except ModuleNotFound as e: except ModuleNotFound as e:
analysis.add(self._evaluator, 'import-error', e.name_part) analysis.add(self._evaluator, 'import-error', e.name)
return [] return []
if module is None: if module is None:
return [] return []
if self._import.is_nested() and not self.nested_resolve: #if self._import.is_nested() and not self.nested_resolve:
scopes = [NestedImportModule(module, self._import)] # scopes = [NestedImportModule(module, self._import)]
else: #else:
scopes = [module] scopes = [module]
#star_imports = remove_star_imports(self._evaluator, module) #star_imports = remove_star_imports(self._evaluator, module)
#if star_imports: #if star_imports:
@@ -127,7 +127,11 @@ class ImportWrapper(pr.Base):
self._evaluator.find_types(s, rest[0], is_goto=True) self._evaluator.find_types(s, rest[0], is_goto=True)
for s in scopes)) for s in scopes))
else: else:
scopes = importer.follow_rest(scopes[0], rest) print(self._import, scopes, rest)
if self._import.type == 'import_from':
scopes = importer.follow_rest(scopes[0], rest)
else:
scopes = []
debug.dbg('after import: %s', scopes) debug.dbg('after import: %s', scopes)
if not scopes: if not scopes:
analysis.add(self._evaluator, 'import-error', importer.import_path[-1]) analysis.add(self._evaluator, 'import-error', importer.import_path[-1])
@@ -135,6 +139,7 @@ class ImportWrapper(pr.Base):
self._evaluator.recursion_detector.pop_stmt() self._evaluator.recursion_detector.pop_stmt()
return scopes return scopes
class ImportWrapper2(pr.Base): class ImportWrapper2(pr.Base):
""" """
An ImportWrapper is the path of a `pr.Import` object. An ImportWrapper is the path of a `pr.Import` object.
@@ -284,7 +289,7 @@ class ImportWrapper2(pr.Base):
try: try:
module, rest = self._importer.follow_file_system() module, rest = self._importer.follow_file_system()
except ModuleNotFound as e: except ModuleNotFound as e:
analysis.add(self._evaluator, 'import-error', e.name_part) analysis.add(self._evaluator, 'import-error', e.name)
return [] return []
if module is None: if module is None:
@@ -346,7 +351,7 @@ 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) for name 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
@@ -435,7 +440,7 @@ class _Importer(object):
@property @property
def str_import_path(self): def str_import_path(self):
"""Returns the import path as pure strings instead of `Name`.""" """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) for name in self.import_path)
def get_relative_path(self): def get_relative_path(self):
path = self.file_path path = self.file_path
+11 -10
View File
@@ -992,16 +992,7 @@ class Import(Simple):
raise ValueError('Name should be defined in the import itself') raise ValueError('Name should be defined in the import itself')
def is_nested(self): def is_nested(self):
""" return False # By default, sub classes may overwrite this behavior
This checks for the special case of nested imports, without aliases and
from statement::
import foo.bar
"""
return False
# TODO use this check differently?
return not self.alias and not self.from_names \
and len(self.namespace_names) > 1
def is_star_import(self): def is_star_import(self):
return self.children[-1] == '*' return self.children[-1] == '*'
@@ -1102,6 +1093,16 @@ class ImportName(Import):
# dotted_names # dotted_names
yield as_name.children[::2], alias yield as_name.children[::2], alias
def is_nested(self):
"""
This checks for the special case of nested imports, without aliases and
from statement::
import foo.bar
"""
return [1 for path, alias in self._dotted_as_names()
if alias is None and len(path) > 1]
def aliases(self): def aliases(self):
return dict((alias, path[-1]) for path, alias in self._dotted_as_names() return dict((alias, path[-1]) for path, alias in self._dotted_as_names()
if alias is not None) if alias is not None)