diff --git a/jedi/inference/dynamic_params.py b/jedi/inference/dynamic_params.py index e37c442e..407200b7 100644 --- a/jedi/inference/dynamic_params.py +++ b/jedi/inference/dynamic_params.py @@ -21,13 +21,13 @@ from jedi import settings from jedi import debug from jedi.parser_utils import get_parent_scope from jedi.inference.cache import inference_state_method_cache -from jedi.inference import imports from jedi.inference.arguments import TreeArguments from jedi.inference.param import get_executed_param_names from jedi.inference.helpers import is_stdlib_path from jedi.inference.utils import to_list from jedi.inference.value import instance from jedi.inference.base_value import ValueSet, NO_VALUES +from jedi.inference.references import get_module_contexts_containing_name from jedi.inference import recursion @@ -116,7 +116,7 @@ def _search_function_arguments(module_context, funcdef, string_name): found_arguments = False i = 0 inference_state = module_context.inference_state - for for_mod_context in imports.get_module_contexts_containing_name( + for for_mod_context in get_module_contexts_containing_name( inference_state, [module_context], string_name): for name, trailer in _get_potential_nodes(for_mod_context, string_name): i += 1 diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index c6ef0244..3fb8584e 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -498,64 +498,6 @@ def load_module_from_path(inference_state, file_io, base_names=None): return module -def get_module_contexts_containing_name(inference_state, module_contexts, name): - """ - Search a name in the directories of modules. - """ - def check_directory(folder_io): - for file_name in folder_io.list(): - if file_name.endswith('.py'): - yield folder_io.get_file_io(file_name) - - def check_fs(file_io, base_names): - try: - code = file_io.read() - except FileNotFoundError: - return None - code = python_bytes_to_unicode(code, errors='replace') - if name not in code: - return None - new_file_io = KnownContentFileIO(file_io.path, code) - m = load_module_from_path(inference_state, new_file_io, base_names) - if isinstance(m, compiled.CompiledObject): - return None - return m.as_context() - - # skip non python modules - used_mod_paths = set() - folders_with_names_to_be_checked = [] - for module_context in module_contexts: - path = module_context.py__file__() - if path not in used_mod_paths: - file_io = module_context.get_value().file_io - if file_io is not None: - used_mod_paths.add(path) - folders_with_names_to_be_checked.append(( - file_io.get_parent_folder(), - module_context.get_value().py__package__() - )) - yield module_context - - if not settings.dynamic_params_for_other_modules: - return - - def get_file_ios_to_check(): - for folder_io, base_names in folders_with_names_to_be_checked: - for file_io in check_directory(folder_io): - if file_io.path not in used_mod_paths: - yield file_io, base_names - - for p in settings.additional_dynamic_modules: - p = os.path.abspath(p) - if p not in used_mod_paths: - yield FileIO(p), None - - for file_io, base_names in get_file_ios_to_check(): - m = check_fs(file_io, base_names) - if m is not None: - yield m - - def follow_error_node_imports_if_possible(context, name): error_node = tree.search_ancestor(name, 'error_node') if error_node is not None: diff --git a/jedi/inference/references.py b/jedi/inference/references.py index 20b287f3..dab5369f 100644 --- a/jedi/inference/references.py +++ b/jedi/inference/references.py @@ -1,4 +1,11 @@ -from jedi.inference import imports +import os + +from parso import python_bytes_to_unicode + +from jedi import settings +from jedi.file_io import FileIO, KnownContentFileIO +from jedi.inference.imports import SubModuleName, load_module_from_path +from jedi.inference.compiled import CompiledObject from jedi.inference.filters import ParserTreeFilter from jedi.inference.gradual.conversion import convert_names @@ -10,7 +17,7 @@ def _resolve_names(definition_names, avoid_names=()): # on the same module. continue - if not isinstance(name, imports.SubModuleName): + if not isinstance(name, SubModuleName): # SubModuleNames are not actually existing names but created # names when importing something like `import foo.bar.baz`. yield name @@ -109,7 +116,7 @@ def find_references(module_context, tree_name): module_contexts = set(m for m in module_contexts if not m.is_compiled()) non_matching_reference_maps = {} - potential_modules = imports.get_module_contexts_containing_name( + potential_modules = get_module_contexts_containing_name( inf, module_contexts, search_name ) for module_context in potential_modules: @@ -130,3 +137,61 @@ def find_references(module_context, tree_name): for name in new: non_matching_reference_maps.setdefault(name, []).append(new) return found_names_dct.values() + + +def get_module_contexts_containing_name(inference_state, module_contexts, name): + """ + Search a name in the directories of modules. + """ + def check_directory(folder_io): + for file_name in folder_io.list(): + if file_name.endswith('.py'): + yield folder_io.get_file_io(file_name) + + def check_fs(file_io, base_names): + try: + code = file_io.read() + except FileNotFoundError: + return None + code = python_bytes_to_unicode(code, errors='replace') + if name not in code: + return None + new_file_io = KnownContentFileIO(file_io.path, code) + m = load_module_from_path(inference_state, new_file_io, base_names) + if isinstance(m, CompiledObject): + return None + return m.as_context() + + # skip non python modules + used_mod_paths = set() + folders_with_names_to_be_checked = [] + for module_context in module_contexts: + path = module_context.py__file__() + if path not in used_mod_paths: + file_io = module_context.get_value().file_io + if file_io is not None: + used_mod_paths.add(path) + folders_with_names_to_be_checked.append(( + file_io.get_parent_folder(), + module_context.get_value().py__package__() + )) + yield module_context + + if not settings.dynamic_params_for_other_modules: + return + + def get_file_ios_to_check(): + for folder_io, base_names in folders_with_names_to_be_checked: + for file_io in check_directory(folder_io): + if file_io.path not in used_mod_paths: + yield file_io, base_names + + for p in settings.additional_dynamic_modules: + p = os.path.abspath(p) + if p not in used_mod_paths: + yield FileIO(p), None + + for file_io, base_names in get_file_ios_to_check(): + m = check_fs(file_io, base_names) + if m is not None: + yield m diff --git a/test/completion/import_tree/references.py b/test/completion/import_tree/references.py new file mode 100644 index 00000000..dd3c9838 --- /dev/null +++ b/test/completion/import_tree/references.py @@ -0,0 +1,5 @@ +from ..usages import usage_definition + + +def x(): + usage_definition() diff --git a/test/completion/on_import.py b/test/completion/on_import.py index aa96883d..76984b12 100644 --- a/test/completion/on_import.py +++ b/test/completion/on_import.py @@ -75,7 +75,7 @@ from import_tree.pkg.mod1 import not_existant, # whitespace before from import_tree.pkg.mod1 import not_existant, #? 22 ['mod1', 'base'] from import_tree.pkg. import mod1 -#? 17 ['mod1', 'mod2', 'random', 'pkg', 'rename1', 'rename2', 'classes', 'globals', 'recurse_class1', 'recurse_class2', 'invisible_pkg', 'flow_import'] +#? 17 ['mod1', 'mod2', 'random', 'pkg', 'references', 'rename1', 'rename2', 'classes', 'globals', 'recurse_class1', 'recurse_class2', 'invisible_pkg', 'flow_import'] from import_tree. import pkg #? 18 ['pkg'] diff --git a/test/completion/usages.py b/test/completion/usages.py index c3afb8d6..312765b2 100644 --- a/test/completion/usages.py +++ b/test/completion/usages.py @@ -367,3 +367,13 @@ in_python from stub_folder.with_stub_folder.nested_with_stub import in_both #< ('stub_folder.with_stub_folder.nested_with_stub', 2, 0), ('stub:stub_folder.with_stub_folder.nested_with_stub', 2, 0), ('stubs', 66, 17), (-2, 58), (0, 0) in_both + +# ----------------- +# across directories +# ----------------- + +#< 8 (0, 0), (3, 4), ('import_tree.references', 1, 21), ('import_tree.references', 5, 4) +usage_definition = 1 +if False: + #< 8 (-3, 0), (0, 4), ('import_tree.references', 1, 21), ('import_tree.references', 5, 4) + usage_definition() diff --git a/test/test_inference/test_imports.py b/test/test_inference/test_imports.py index 17d40500..e9edaf8e 100644 --- a/test/test_inference/test_imports.py +++ b/test/test_inference/test_imports.py @@ -13,6 +13,7 @@ from jedi.inference import compiled from jedi.inference import imports from jedi.api.project import Project from jedi.inference.gradual.conversion import _stub_to_python_value_set +from jedi.inference.references import get_module_contexts_containing_name from ..helpers import cwd_at, get_example_dir, test_dir, root_dir THIS_DIR = os.path.dirname(__file__) @@ -319,7 +320,7 @@ def test_get_modules_containing_name(inference_state, path, goal, is_package): ) assert module module_context = module.as_context() - input_module, found_module = imports.get_module_contexts_containing_name( + input_module, found_module = get_module_contexts_containing_name( inference_state, [module_context], 'string_that_only_exists_here'