diff --git a/jedi/evaluate/compiled/context.py b/jedi/evaluate/compiled/context.py index 6ae75722..25949af7 100644 --- a/jedi/evaluate/compiled/context.py +++ b/jedi/evaluate/compiled/context.py @@ -305,20 +305,21 @@ class CompiledObjectFilter(AbstractFilter): name, lambda: self._compiled_object.access_handle.is_allowed_getattr(name), lambda: self._compiled_object.access_handle.dir(), + check_has_attribute=True ) - def _get(self, name, allowed_getattr_callback, dir_callback): + def _get(self, name, allowed_getattr_callback, dir_callback, check_has_attribute=False): """ To remove quite a few access calls we introduced the callback here. """ has_attribute, is_descriptor = allowed_getattr_callback() - if not has_attribute: + if check_has_attribute and not has_attribute: return [] # Always use unicode objects in Python 2 from here. name = force_unicode(name) - if is_descriptor: + if is_descriptor or not has_attribute: return [self._get_cached_name(name, is_empty=True)] if self._is_instance and name not in dir_callback(): diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index 419eae50..31b936a9 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -298,3 +298,27 @@ def test_repr_execution_issue(): d, = script.goto_definitions() assert d.name == 'ErrorRepr' assert d.type == 'instance' + + +def test_dir_magic_method(): + class CompleteAttrs(object): + def __getattr__(self, name): + if name == 'foo': + return 1 + if name == 'bar': + return 2 + raise AttributeError(name) + + def __dir__(self): + return ['foo', 'bar'] + object.__dir__(self) + + itp = jedi.Interpreter("ca.", [{'ca': CompleteAttrs()}]) + completions = itp.completions() + names = [c.name for c in completions] + assert '__dir__' in names + assert '__eq__' in names + assert 'foo' in names + assert 'bar' in names + + foo = [c for c in completions if c.name == 'foo'][0] + assert foo._goto_definitions() == []