diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index 5a0a5fd8..be42e43d 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -95,12 +95,12 @@ def _check_for_setattr(instance): """ Check if there's any setattr method inside an instance. If so, return True. """ - from jedi.evaluate.context import ModuleContext module = instance.get_root_context() - if not isinstance(module, ModuleContext): + node = module.tree_node + if node is None: + # If it's a compiled module or doesn't have a tree_node return False - node = module.tree_node try: stmt_names = node.get_used_names()['setattr'] except KeyError: diff --git a/jedi/evaluate/context/namespace.py b/jedi/evaluate/context/namespace.py index c0ec2fe6..3fbd25ef 100644 --- a/jedi/evaluate/context/namespace.py +++ b/jedi/evaluate/context/namespace.py @@ -31,7 +31,7 @@ class ImplicitNamespaceContext(Context): super(ImplicitNamespaceContext, self).__init__(evaluator, parent_context=None) self.evaluator = evaluator self._fullname = fullname - self.paths = paths + self._paths = paths def get_filters(self, search_global=False, until_position=None, origin_scope=None): yield DictFilter(self._sub_modules_dict()) @@ -51,7 +51,7 @@ class ImplicitNamespaceContext(Context): return self._fullname def py__path__(self): - return [self.paths] + return self._paths def py__name__(self): return self._fullname @@ -60,7 +60,7 @@ class ImplicitNamespaceContext(Context): def _sub_modules_dict(self): names = {} - file_names = chain.from_iterable(os.listdir(path) for path in self.paths) + file_names = chain.from_iterable(os.listdir(path) for path in self._paths) mods = [ file_name.rpartition('.')[0] if '.' in file_name else file_name for file_name in file_names diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 707e08e5..865494cc 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -328,8 +328,6 @@ class Importer(object): :param only_modules: Indicates wheter it's possible to import a definition that is not defined in a module. """ - from jedi.evaluate.context import ModuleContext - from jedi.evaluate.context.namespace import ImplicitNamespaceContext names = [] if self.import_path: # flask @@ -351,15 +349,13 @@ class Importer(object): if context.api_type != 'module': # not a module continue # namespace packages - if isinstance(context, ModuleContext) \ - and context.py__file__().endswith('__init__.py'): - paths = context.py__path__() - names += self._get_module_names(paths, in_module=context) - - # implicit namespace packages - elif isinstance(context, ImplicitNamespaceContext): - paths = context.paths - names += self._get_module_names(paths, in_module=context) + try: + path_method = context.py__path__ + except AttributeError: + pass + else: + # For implicit namespace packages and module names. + names += self._get_module_names(path_method(), in_module=context) if only_modules: # In the case of an import like `from x.` we don't need to diff --git a/jedi/evaluate/usages.py b/jedi/evaluate/usages.py index 290c4695..d7604a0d 100644 --- a/jedi/evaluate/usages.py +++ b/jedi/evaluate/usages.py @@ -1,6 +1,5 @@ from jedi.evaluate import imports from jedi.evaluate.filters import TreeNameDefinition -from jedi.evaluate.context import ModuleContext def _resolve_names(definition_names, avoid_names=()): @@ -39,7 +38,7 @@ def usages(module_context, tree_name): search_name = tree_name.value found_names = _find_names(module_context, tree_name) modules = set(d.get_root_context() for d in found_names.values()) - modules = set(m for m in modules if isinstance(m, ModuleContext)) + modules = set(m for m in modules if m.is_module()) non_matching_usage_maps = {} for m in imports.get_modules_containing_name(module_context.evaluator, modules, search_name): diff --git a/test/test_api/test_classes.py b/test/test_api/test_classes.py index 9b8c1561..d44f70b9 100644 --- a/test/test_api/test_classes.py +++ b/test/test_api/test_classes.py @@ -65,27 +65,31 @@ def test_basedefinition_type(Script, environment): 'generator', 'statement', 'import', 'param') -def test_basedefinition_type_import(Script): - def get_types(source, **kwargs): - return {t.type for t in Script(source, **kwargs).completions()} +@pytest.mark.parametrize( + ('src', 'expected_result', 'column'), [ + # import one level + ('import t', 'module', None), + ('import ', 'module', None), + ('import datetime; datetime', 'module', None), - # import one level - assert get_types('import t') == {'module'} - assert get_types('import ') == {'module'} - assert get_types('import datetime; datetime') == {'module'} + # from + ('from datetime import timedelta', 'class', None), + ('from datetime import timedelta; timedelta', 'class', None), + ('from json import tool', 'module', None), + ('from json import tool; tool', 'module', None), - # from - assert get_types('from datetime import timedelta') == {'class'} - assert get_types('from datetime import timedelta; timedelta') == {'class'} - assert get_types('from json import tool') == {'module'} - assert get_types('from json import tool; tool') == {'module'} + # import two levels + ('import json.tool; json', 'module', None), + ('import json.tool; json.tool', 'module', None), + ('import json.tool; json.tool.main', 'function', None), + ('import json.tool', 'module', None), + ('import json.tool', 'module', 9), + ] - # import two levels - assert get_types('import json.tool; json') == {'module'} - assert get_types('import json.tool; json.tool') == {'module'} - assert get_types('import json.tool; json.tool.main') == {'function'} - assert get_types('import json.tool') == {'module'} - assert get_types('import json.tool', column=9) == {'module'} +) +def test_basedefinition_type_import(Script, src, expected_result, column): + types = {t.type for t in Script(src, column=column).completions()} + assert types == {expected_result} def test_function_call_signature_in_doc(Script):