diff --git a/jedi/evaluate/compiled/access.py b/jedi/evaluate/compiled/access.py index 07a781d5..69066e6e 100644 --- a/jedi/evaluate/compiled/access.py +++ b/jedi/evaluate/compiled/access.py @@ -114,9 +114,8 @@ def compiled_objects_cache(attribute_name): return decorator -@compiled_objects_cache('compiled_cache') def create_access(evaluator, obj): - return DirectObjectAccess(evaluator, obj) + return evaluator.compiled_subprocess.get_or_create_access_handle(obj) def load_module(evaluator, path=None, name=None): @@ -329,9 +328,10 @@ class DirectObjectAccess(object): return list(reversed(list(get()))) - def execute_operation(self, other, operator): + def execute_operation(self, other_access_handle, operator): + other_access = other_access_handle.access op = _OPERATORS[operator] - return self._create_access_path(op(self._obj, other._obj)) + return self._create_access_path(op(self._obj, other_access._obj)) def needs_type_completions(self): return inspect.isclass(self._obj) and self._obj != type diff --git a/jedi/evaluate/compiled/subprocess/__init__.py b/jedi/evaluate/compiled/subprocess/__init__.py index d0fb2c94..1669af89 100644 --- a/jedi/evaluate/compiled/subprocess/__init__.py +++ b/jedi/evaluate/compiled/subprocess/__init__.py @@ -16,7 +16,7 @@ from functools import partial from jedi.cache import memoize_method from jedi.evaluate.compiled.subprocess import functions -from jedi.evaluate.compiled import CompiledObject +from jedi.evaluate.compiled.access import DirectObjectAccess _PICKLE_PROTOCOL = 2 @@ -37,24 +37,39 @@ def _get_function(name): return getattr(functions, name) -class EvaluatorSameProcess(object): +class _EvaluatorProcess(object): + def __init__(self, evaluator): + self._evaluator_weakref = weakref.ref(evaluator) + self._evaluator_id = id(evaluator) + self._handles = {} + + def get_or_create_access_handle(self, obj): + id_ = id(obj) + try: + return self.get_access_handle(id_) + except KeyError: + access = DirectObjectAccess(self._evaluator_weakref(), obj) + handle = self._handles[id_] = AccessHandle(self, access, id_) + return handle + + def get_access_handle(self, id_): + return self._handles[id_] + + +class EvaluatorSameProcess(_EvaluatorProcess): """ Basically just an easy access to functions.py. It has the same API as EvaluatorSubprocess and does the same thing without using a subprocess. This is necessary for the Interpreter process. """ - def __init__(self, evaluator): - self._evaluator = evaluator - def __getattr__(self, name): - return partial(_get_function(name), self._evaluator, IgnoreHandles()) + return partial(_get_function(name), self._evaluator_weakref()) -class EvaluatorSubprocess(object): +class EvaluatorSubprocess(_EvaluatorProcess): def __init__(self, evaluator, compiled_subprocess): + super(EvaluatorSubprocess).__init__(evaluator) self._used = False - self._evaluator_weakref = weakref.ref(evaluator) - self._evaluator_id = id(evaluator) self._compiled_subprocess = compiled_subprocess def __getattr__(self, name): @@ -70,7 +85,7 @@ class EvaluatorSubprocess(object): kwargs=kwargs, unpickler=lambda stdout: ModifiedUnpickler(self, stdout).load() ) - if isinstance(result, CompiledHandle): + if isinstance(result, AccessHandle): result.add_subprocess(self) return result @@ -142,12 +157,11 @@ class Listener(): from jedi.evaluate import Evaluator, project try: - evaluator, handles = self._evaluators[evaluator_id] + evaluator = self._evaluators[evaluator_id] except KeyError: evaluator = Evaluator(None, project=project.Project()) - handles = Handles() - self._evaluators[evaluator_id] = evaluator, handles - return evaluator, handles + self._evaluators[evaluator_id] = evaluator + return evaluator def _run(self, evaluator_id, function, args, kwargs): if evaluator_id is None: @@ -155,8 +169,8 @@ class Listener(): elif function is None: del self._evaluators[evaluator_id] else: - evaluator, handles = self._get_evaluator(function, evaluator_id) - return function(evaluator, handles, *args, **kwargs) + evaluator = self._get_evaluator(function, evaluator_id) + return function(evaluator, *args, **kwargs) def listen(self): stdout = sys.stdout @@ -194,44 +208,26 @@ class ModifiedUnpickler(pickle._Unpickler): super(ModifiedUnpickler, self).load_newobj() tos = self.stack[-1] print('pop', tos, file=sys.stderr) - if isinstance(tos, CompiledHandle): + if isinstance(tos, AccessHandle): tos.add_subprocess(self._subprocess) dispatch[pickle.NEWOBJ[0]] = load_newobj class ModifiedPickler(pickle._Pickler): + def __init__(self, subprocess, *args, **kwargs): + super(ModifiedPickler, self).__init__(*args, **kwargs) + self._subprocess = subprocess + def save(self, obj, *args, **kwargs): print('s', obj, args, kwargs, file=sys.stderr) - if isinstance(obj, CompiledObject): - obj = CompiledHandle(obj) return super(ModifiedPickler, self).save(obj, *args, **kwargs) -class Handles(object): - def __init__(self): - self._handles = {} - - def create(self, obj): - handle = self._handles[id(obj)] = CompiledHandle(obj) - return handle - - def get_compiled_object(self, id_): - return self._handles[id_].compiled_object - - -class IgnoreHandles(object): - def create(self, obj): - return obj - - -class CompiledHandle(object): - def __init__(self, compiled_object): - self.compiled_object = compiled_object - self._id = id(compiled_object) - - @property - def obj(self): - raise NotImplementedError +class AccessHandle(object): + def __init__(self, subprocess, access, id_): + self.access = access + self._subprocess = subprocess + self._id = id_ def add_subprocess(self, subprocess): self._subprocess = subprocess @@ -244,15 +240,6 @@ class CompiledHandle(object): def __getattr__(self, name): print('getattr', name, file=sys.stderr) - from jedi.evaluate import compiled - attr = getattr(compiled.CompiledObject, name) - if isinstance(attr, property): - return self._subprocess.get_compiled_property(self._id, name) - elif isinstance(attr, compiled.CheckAttribute): - # It might raise an AttributeError, however we're interested in the - # function return value. - self._subprocess.get_compiled_property(self._id, name) - def compiled_method(*args, **kwargs): return self._subprocess.get_compiled_method_return(self._id, name, *args, **kwargs) return compiled_method diff --git a/jedi/evaluate/compiled/subprocess/functions.py b/jedi/evaluate/compiled/subprocess/functions.py index 53332df8..1f21381a 100644 --- a/jedi/evaluate/compiled/subprocess/functions.py +++ b/jedi/evaluate/compiled/subprocess/functions.py @@ -13,14 +13,9 @@ def import_module(evaluator, handles, path=None, name=None): return handles.create(compiled_object) -def get_compiled_property(evaluator, handles, id, attribute): - compiled_object = handles.get_compiled_object(id) - return getattr(compiled_object, attribute) - - -def get_compiled_method_return(evaluator, handles, id, attribute, *args, **kwargs): - compiled_object = handles.get_compiled_object(id) - return getattr(compiled_object, attribute)(*args, **kwargs) +def get_compiled_method_return(evaluator, id, attribute, *args, **kwargs): + handle = evaluator.compiled_subprocess.get_access_handle(id) + return getattr(handle.access, attribute)(*args, **kwargs) def get_special_object(evaluator, handles, identifier): diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 3a812ff5..640773e9 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -378,8 +378,7 @@ class Importer(object): module = ImplicitNamespaceContext(self._evaluator, fullname=fullname) module.paths = paths elif module_file is None and not module_path.endswith(('.py', '.zip', '.egg')): - module = self._evaluator.compiled_subprocess.import_module(module_path) - #module = compiled.load_module(self._evaluator, module_path) + module = compiled.load_module(self._evaluator, path=module_path) else: module = _load_module(self._evaluator, module_path, code, sys_path, parent_module)