diff --git a/jedi/api.py b/jedi/api.py index e4f7f532..4c1af950 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -54,13 +54,13 @@ class Script(object): :type source_encoding: str """ def __init__(self, source, line, column, source_path, - source_encoding='utf-8'): + source_encoding='utf-8', fast=True): api_classes._clear_caches() debug.reset_time() self.source = modules.source_to_unicode(source, source_encoding) self.pos = line, column - self._module = modules.ModuleWithCursor(source_path, - source=self.source, position=self.pos) + self._module = modules.ModuleWithCursor( + source_path, source=self.source, position=self.pos, fast=fast) self._source_path = source_path self.source_path = None if source_path is None \ else os.path.abspath(source_path) @@ -503,6 +503,59 @@ class Script(object): return sorted(d, key=lambda x: (x.module_path or '', x.start_pos)) +class Interpreter(Script): + + """ + Jedi API for Python REPLs. + + >>> from itertools import chain + >>> script = Interpreter('cha', [locals()]) + >>> script.complete() + [] + + """ + + def __init__(self, source, namespaces=[], line=None, column=None, + source_path=None, source_encoding='utf-8'): + lines = source.splitlines() + line = len(lines) if line is None else line + column = len(lines[-1]) if column is None else column + super(Interpreter, self).__init__( + source, line, column, source_path, source_encoding, fast=False) + for ns in namespaces: + self._import_raw_namespace(ns) + + def _import_raw_namespace(self, raw_namespace): + for (variable, obj) in raw_namespace.items(): + try: + module = obj.__module__ + except: + continue + fakeimport = self._make_fakeimport(variable, module) + self._parser.scope.imports.append(fakeimport) + + def _make_fakeimport(self, variable, module): + submodule = self._parser.scope._sub_module + varname = pr.Name( + module=submodule, + names=[(variable, (0, 0))], + start_pos=(0, 0), + end_pos=(None, None)) + modname = pr.Name( + module=submodule, + names=[(module, (0, 0))], + start_pos=(0, 0), + end_pos=(None, None)) + fakeimport = pr.Import( + module=submodule, + namespace=varname, + from_ns=modname, + start_pos=(0, 0), + end_pos=(None, None)) + fakeimport.parent = submodule + return fakeimport + + def defined_names(source, source_path=None, source_encoding='utf-8'): """ Get all definitions in `source` sorted by its position. diff --git a/jedi/modules.py b/jedi/modules.py index 37e5bbac..035a894e 100644 --- a/jedi/modules.py +++ b/jedi/modules.py @@ -92,8 +92,10 @@ class ModuleWithCursor(Module): :param position: The position, the user is currently in. Only important \ for the main file. """ - def __init__(self, path, source, position): + def __init__(self, path, source, position, fast): super(ModuleWithCursor, self).__init__(path, source) + self._parserclass = fast_parser.FastParser if fast else \ + parsing.Parser self.position = position # this two are only used, because there is no nonlocal in Python 2 @@ -112,7 +114,7 @@ class ModuleWithCursor(Module): # Call the parser already here, because it will be used anyways. # Also, the position is here important (which will not be used by # default), therefore fill the cache here. - self._parser = fast_parser.FastParser(self.source, self.path, + self._parser = self._parserclass(self.source, self.path, self.position) # don't pickle that module, because it's changing fast cache.save_module(self.path, self.name, self._parser,