diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 54b74302..88404769 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -82,7 +82,7 @@ class NameFinder(object): name_list = sorted(name_list, key=lambda n: n.start_pos, reverse=True) for name in name_list: - if unicode(self.name_str) != name.get_code(): + if unicode(self.name_str) != unicode(name): continue stmt = name.get_definition() @@ -233,6 +233,7 @@ class NameFinder(object): for name in names: typ = name.get_definition() + print(typ) if typ.isinstance(pr.ForFlow): types += self._handle_for_loops(typ) elif isinstance(typ, pr.Param): @@ -243,6 +244,8 @@ class NameFinder(object): types += evaluator.find_types(typ.parent.parent, str(name)) else: types += self._remove_statements(typ, name) + elif isinstance(typ, pr.Import): + types += imports.ImportWrapper(self._evaluator, name).follow() else: if typ.isinstance(er.Function) and resolve_decorator: typ = typ.get_decorated_func() diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 0c77494e..bd6ca11e 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -38,7 +38,73 @@ class ModuleNotFound(Exception): self.name_part = name_part -class ImportWrapper(pr.Base): +class ImportWrapper(): + def __init__(self, evaluator, name): + self._evaluator = evaluator + self._name = name + + self._import = name.get_parent_until(pr.Import) + + @memoize_default() + def follow(self, is_goto=False): + if self._evaluator.recursion_detector.push_stmt(self._import): + # check recursion + return [] + + try: + module = self._import.get_parent_until() + import_path = self._import.path_for_name(self._name) + importer = get_importer(self._evaluator, tuple(import_path), module, level=0) + try: + module, rest = importer.follow_file_system() + except ModuleNotFound as e: + analysis.add(self._evaluator, 'import-error', e.name_part) + return [] + + if module is None: + return [] + + 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: + 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)) + 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 + +class ImportWrapper2(pr.Base): """ An ImportWrapper is the path of a `pr.Import` object. """ diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 4c30b52d..fbb07b06 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -212,7 +212,7 @@ class Name(_Leaf): self.start_pos[0], self.start_pos[1]) def get_definition(self): - return self.get_parent_until((ArrayStmt, StatementElement), reverse=True) + return self.parent.get_parent_until((ArrayStmt, StatementElement), reverse=True) class Literal(_Leaf): @@ -416,6 +416,8 @@ class Scope(Simple, DocstringMixin): for c in self.children: if isinstance(c, ExprStmt): names += c.get_defined_names() + elif isinstance(c, (Function, Class)): + names.append(c.name) return names @Python3Method @@ -844,7 +846,8 @@ class Import(Simple): if self.children[0] == 'import': return self.children[1:] else: # from - raise NotImplementedError +# , , , + return [self.children[-1]] # TODO remove if self.defunct: @@ -868,6 +871,23 @@ class Import(Simple): n.append(self.alias) return n + def _paths(self): + if self.children[0] == 'import': + return [self.children[1:]] + else: + raise NotImplementedError + + def path_for_name(self, name): + for path in self._paths(): + if name in path: + return path + + @property + def level(self): + """The level parameter of ``__import__``.""" + # TODO implement + return 0 + def is_nested(self): """ This checks for the special case of nested imports, without aliases and @@ -875,6 +895,8 @@ class Import(Simple): 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