diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 82229ba7..057a6b0a 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -5,8 +5,8 @@ import re from jedi._compatibility import builtins as _builtins from jedi.evaluate.compiled.context import CompiledObject, CompiledName, \ - CompiledObjectFilter, CompiledContextName, create_from_access -from jedi.evaluate.compiled.access import create_access + CompiledObjectFilter, CompiledContextName, create_from_access_path +from jedi.evaluate.compiled.access import create_access_path from jedi import debug @@ -32,8 +32,8 @@ def create_simple_object(evaluator, obj): def create(evaluator, obj): - return create_from_access( - evaluator, create_access(evaluator, obj) + return create_from_access_path( + evaluator, create_access_path(evaluator, obj) ) diff --git a/jedi/evaluate/compiled/access.py b/jedi/evaluate/compiled/access.py index ab41e21c..6feff39f 100644 --- a/jedi/evaluate/compiled/access.py +++ b/jedi/evaluate/compiled/access.py @@ -109,6 +109,16 @@ def create_access(evaluator, obj): return DirectObjectAccess(evaluator, obj) +class AccessPath(object): + def __init__(self, accesses): + self.accesses = accesses + + +def create_access_path(evaluator, obj): + access = create_access(evaluator, obj) + return AccessPath(access.get_access_path_tuples()) + + class DirectObjectAccess(object): def __init__(self, evaluator, obj): self._evaluator = evaluator @@ -120,6 +130,9 @@ class DirectObjectAccess(object): def _create_access(self, obj): return create_access(self._evaluator, obj) + def _create_access_path(self, obj): + return create_access_path(self._evaluator, obj) + def py__bool__(self): return bool(self._obj) @@ -150,14 +163,14 @@ class DirectObjectAccess(object): return None def py__mro__accesses(self): - return tuple(self._create_access(cls) for cls in self._obj.__mro__[1:]) + return tuple(self._create_access_path(cls) for cls in self._obj.__mro__[1:]) def py__getitem__(self, index): if type(self._obj) not in (str, list, tuple, unicode, bytes, bytearray, dict): # Get rid of side effects, we won't call custom `__getitem__`s. return None - return self._create_access(self._obj[index]) + return self._create_access_path(self._obj[index]) def py__iter__list(self): if type(self._obj) not in (str, list, tuple, unicode, bytes, bytearray, dict): @@ -169,14 +182,14 @@ class DirectObjectAccess(object): if i > 20: # Should not go crazy with large iterators break - lst.append(self._create_access(part)) + lst.append(self._create_access_path(part)) return lst def py__class__(self): - return self._create_access(self._obj.__class__) + return self._create_access_path(self._obj.__class__) def py__bases__(self): - return [self._create_access(base) for base in self._obj.__bases__] + return [self._create_access_path(base) for base in self._obj.__bases__] def get_repr(self): return repr(self._obj) @@ -239,14 +252,10 @@ class DirectObjectAccess(object): return 'instance' def get_access_path_tuples(self): - path = self._get_objects_path() - try: - # Just provoke an AttributeError. - result = [(o.__name__, o) for o in path] - except AttributeError: - return [] - else: - return [(name, self._create_access(o)) for name, o in result] + return [ + (getattr(o, '__name__', None), create_access(self._evaluator, o)) + for o in self._get_objects_path() + ] def _get_objects_path(self): def get(): @@ -272,6 +281,7 @@ class DirectObjectAccess(object): yield builtins else: try: + # TODO use sys.modules, __module__ can be faked. yield __import__(imp_plz) except ImportError: # __module__ can be something arbitrary that doesn't exist. @@ -281,7 +291,7 @@ class DirectObjectAccess(object): def execute_operation(self, other, operator): op = _OPERATORS[operator] - return self._create_access(op(self._obj, other._obj)) + return self._create_access_path(op(self._obj, other._obj)) def needs_type_completions(self): return inspect.isclass(self._obj) and self._obj != type @@ -311,17 +321,17 @@ class DirectObjectAccess(object): SignatureParam( name=p.name, has_default=p.default is not p.empty, - default=self._create_access(p.default), + default=self._create_access_path(p.default), has_annotation=p.annotation is not p.empty, - annotation=self._create_access(p.annotation), + annotation=self._create_access_path(p.annotation), ) for p in signature.parameters.values() ] def negate(self): - return self._create_access(-self._obj) + return self._create_access_path(-self._obj) def dict_values(self): - return [self._create_access(v) for v in self._obj.values()] + return [self._create_access_path(v) for v in self._obj.values()] def is_super_class(self, exception): return issubclass(exception, self._obj) diff --git a/jedi/evaluate/compiled/context.py b/jedi/evaluate/compiled/context.py index 43c63970..52d86828 100644 --- a/jedi/evaluate/compiled/context.py +++ b/jedi/evaluate/compiled/context.py @@ -63,18 +63,18 @@ class CompiledObject(Context): @CheckAttribute def py__class__(self): - return create_from_access(self.evaluator, self.access.py__class__()) + return create_from_access_path(self.evaluator, self.access.py__class__()) @CheckAttribute def py__mro__(self): return (self,) + tuple( - create_from_access(self.evaluator, access) for access in self.access.py__mro__accesses() + create_from_access_path(self.evaluator, access) for access in self.access.py__mro__accesses() ) @CheckAttribute def py__bases__(self): return tuple( - create_from_access(self.evaluator, access) + create_from_access_path(self.evaluator, access) for access in self.access.py__bases__() ) @@ -147,12 +147,12 @@ class CompiledObject(Context): if access is None: return ContextSet() - return ContextSet(create_from_access(self.evaluator, access)) + return ContextSet(create_from_access_path(self.evaluator, access)) @CheckAttribute def py__iter__(self): for access in self.access.py__iter__list(): - yield LazyKnownContext(create_from_access(self.evaluator, access)) + yield LazyKnownContext(create_from_access_path(self.evaluator, access)) def py__name__(self): return self.access.py__name__() @@ -187,7 +187,7 @@ class CompiledObject(Context): def dict_values(self): return ContextSet.from_iterable( - create_from_access(self.evaluator, access) for access in self.access.dict_values() + create_from_access_path(self.evaluator, access) for access in self.access.dict_values() ) def get_safe_value(self, default=_sentinel): @@ -199,13 +199,13 @@ class CompiledObject(Context): return default def execute_operation(self, other, operator): - return create_from_access( + return create_from_access_path( self.evaluator, self.access.execute_operation(other.access, operator) ) def negate(self): - return create_from_access(self.evaluator, self.access.negate()) + return create_from_access_path(self.evaluator, self.access.negate()) def is_super_class(self, exception): return self.access.is_super_class(exception) @@ -251,9 +251,9 @@ class SignatureParamName(AbstractNameDefinition): evaluator = self.parent_context.evaluator contexts = ContextSet() if p.has_default: - contexts = ContextSet(create_from_access(evaluator, p.default)) + contexts = ContextSet(create_from_access_path(evaluator, p.default)) if p.has_annotation: - annotation = create_from_access(evaluator, p.annotation) + annotation = create_from_access_path(evaluator, p.annotation) contexts |= annotation.execute_evaluated() return contexts @@ -416,6 +416,21 @@ def _normalize_create_args(func): return wrapper +def create_from_access_path(evaluator, access_path): + parent_context = None + for name, access in access_path.accesses: + try: + if parent_context is None: + faked = fake.get_faked_module(evaluator.latest_grammar, access_path.accesses[0][0]) + else: + faked = fake.get_faked_with_parent_context(parent_context, name) + except fake.FakeDoesNotExist: + faked = None + + parent_context = create_cached_compiled_object(evaluator, access, parent_context, faked) + return parent_context + + @evaluator_function_cache() def create_from_access(evaluator, access): """ diff --git a/jedi/evaluate/compiled/fake.py b/jedi/evaluate/compiled/fake.py index d888f5b1..5c3edb0c 100644 --- a/jedi/evaluate/compiled/fake.py +++ b/jedi/evaluate/compiled/fake.py @@ -74,10 +74,15 @@ def get_faked_with_parent_context(parent_context, name): raise FakeDoesNotExist -def get_faked_tree_nodes(grammar, string_names): - module = base = _load_faked_module(grammar, string_names[0]) +def get_faked_module(grammar, string_name): + module = _load_faked_module(grammar, string_name) if module is None: raise FakeDoesNotExist + return module + + +def get_faked_tree_nodes(grammar, string_names): + module = base = get_faked_module(grammar, string_names[0]) tree_nodes = [module] for name in string_names[1:]: