From 6818d3affa6c602d435969201195be4bbd2dc598 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Thu, 11 Dec 2014 16:17:07 +0100 Subject: [PATCH] Implement Import.is_nested method. --- jedi/api/__init__.py | 8 ++++---- jedi/evaluate/analysis.py | 7 ++++--- jedi/evaluate/imports.py | 27 ++++++++++++++++----------- jedi/parser/tree.py | 21 +++++++++++---------- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index ddd42f4c..b527a5cc 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -610,11 +610,11 @@ class Script(object): def _analysis(self): #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. - for i in imps: - iw = imports.ImportWrapper(self._evaluator, i, - nested_resolve=True).follow() + for n in imp_names: + iw = imports.ImportWrapper(self._evaluator, n).follow() + i = n.get_definition() 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]) for stmt in sorted(stmts, key=lambda obj: obj.start_pos): diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index 54d34732..60952546 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -212,9 +212,10 @@ def get_module_statements(module): return new stmts = set() - imports = set() + import_names = set() 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(r for r in scope.returns if r is not None) @@ -224,4 +225,4 @@ def get_module_statements(module): pass else: stmts |= add_stmts(decorators) - return stmts, imports + return stmts, import_names diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 2ceb92fe..3846f3a7 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -32,9 +32,9 @@ from jedi.evaluate.cache import memoize_default, NO_DEFAULT class ModuleNotFound(Exception): - def __init__(self, name_part): + def __init__(self, name): super(ModuleNotFound, self).__init__() - self.name_part = name_part + self.name = name def completion_names(evaluator, imp, pos): @@ -101,16 +101,16 @@ class ImportWrapper(pr.Base): try: module, rest = importer.follow_file_system() except ModuleNotFound as e: - analysis.add(self._evaluator, 'import-error', e.name_part) + analysis.add(self._evaluator, 'import-error', e.name) return [] if module is None: return [] - if self._import.is_nested() and not self.nested_resolve: - scopes = [NestedImportModule(module, self._import)] - else: - scopes = [module] + #if self._import.is_nested() and not self.nested_resolve: + # scopes = [NestedImportModule(module, self._import)] + #else: + scopes = [module] #star_imports = remove_star_imports(self._evaluator, module) #if star_imports: @@ -127,7 +127,11 @@ class ImportWrapper(pr.Base): self._evaluator.find_types(s, rest[0], is_goto=True) for s in scopes)) 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) if not scopes: analysis.add(self._evaluator, 'import-error', importer.import_path[-1]) @@ -135,6 +139,7 @@ class ImportWrapper(pr.Base): self._evaluator.recursion_detector.pop_stmt() return scopes + class ImportWrapper2(pr.Base): """ An ImportWrapper is the path of a `pr.Import` object. @@ -284,7 +289,7 @@ class ImportWrapper2(pr.Base): try: module, rest = self._importer.follow_file_system() except ModuleNotFound as e: - analysis.add(self._evaluator, 'import-error', e.name_part) + analysis.add(self._evaluator, 'import-error', e.name) return [] if module is None: @@ -346,7 +351,7 @@ 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) for name 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 @@ -435,7 +440,7 @@ class _Importer(object): @property def str_import_path(self): """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): path = self.file_path diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index a906a3e3..22dc09af 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -992,16 +992,7 @@ class Import(Simple): raise ValueError('Name should be defined in the import itself') def is_nested(self): - """ - 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 + return False # By default, sub classes may overwrite this behavior def is_star_import(self): return self.children[-1] == '*' @@ -1102,6 +1093,16 @@ class ImportName(Import): # dotted_names 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): return dict((alias, path[-1]) for path, alias in self._dotted_as_names() if alias is not None)