You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			593 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			593 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Python
		
	
| """
 | |
| :mod:`jedi.inference.imports` is here to resolve import statements and return
 | |
| the modules/classes/functions/whatever, which they stand for. However there's
 | |
| not any actual importing done. This module is about finding modules in the
 | |
| filesystem. This can be quite tricky sometimes, because Python imports are not
 | |
| always that simple.
 | |
| 
 | |
| This module also supports import autocompletion, which means to complete
 | |
| statements like ``from datetim`` (cursor at the end would return ``datetime``).
 | |
| """
 | |
| import os
 | |
| from pathlib import Path
 | |
| 
 | |
| from parso.python import tree
 | |
| from parso.tree import search_ancestor
 | |
| 
 | |
| from jedi import debug
 | |
| from jedi import settings
 | |
| from jedi.file_io import FolderIO
 | |
| from jedi.parser_utils import get_cached_code_lines
 | |
| from jedi.inference import sys_path
 | |
| from jedi.inference import helpers
 | |
| from jedi.inference import compiled
 | |
| from jedi.inference import analysis
 | |
| from jedi.inference.utils import unite
 | |
| from jedi.inference.cache import inference_state_method_cache
 | |
| from jedi.inference.names import ImportName, SubModuleName
 | |
| from jedi.inference.base_value import ValueSet, NO_VALUES
 | |
| from jedi.inference.gradual.typeshed import import_module_decorator, \
 | |
|     create_stub_module, parse_stub_module
 | |
| from jedi.inference.compiled.subprocess.functions import ImplicitNSInfo
 | |
| from jedi.plugins import plugin_manager
 | |
| 
 | |
| 
 | |
| class ModuleCache:
 | |
|     def __init__(self):
 | |
|         self._name_cache = {}
 | |
| 
 | |
|     def add(self, string_names, value_set):
 | |
|         if string_names is not None:
 | |
|             self._name_cache[string_names] = value_set
 | |
| 
 | |
|     def get(self, string_names):
 | |
|         return self._name_cache.get(string_names)
 | |
| 
 | |
| 
 | |
| # This memoization is needed, because otherwise we will infinitely loop on
 | |
| # certain imports.
 | |
| @inference_state_method_cache(default=NO_VALUES)
 | |
| def infer_import(context, tree_name):
 | |
|     module_context = context.get_root_context()
 | |
|     from_import_name, import_path, level, values = \
 | |
|         _prepare_infer_import(module_context, tree_name)
 | |
|     if values:
 | |
| 
 | |
|         if from_import_name is not None:
 | |
|             values = values.py__getattribute__(
 | |
|                 from_import_name,
 | |
|                 name_context=context,
 | |
|                 analysis_errors=False
 | |
|             )
 | |
| 
 | |
|             if not values:
 | |
|                 path = import_path + (from_import_name,)
 | |
|                 importer = Importer(context.inference_state, path, module_context, level)
 | |
|                 values = importer.follow()
 | |
|     debug.dbg('after import: %s', values)
 | |
|     return values
 | |
| 
 | |
| 
 | |
| @inference_state_method_cache(default=[])
 | |
| def goto_import(context, tree_name):
 | |
|     module_context = context.get_root_context()
 | |
|     from_import_name, import_path, level, values = \
 | |
|         _prepare_infer_import(module_context, tree_name)
 | |
|     if not values:
 | |
|         return []
 | |
| 
 | |
|     if from_import_name is not None:
 | |
|         names = unite([
 | |
|             c.goto(
 | |
|                 from_import_name,
 | |
|                 name_context=context,
 | |
|                 analysis_errors=False
 | |
|             ) for c in values
 | |
|         ])
 | |
|         # Avoid recursion on the same names.
 | |
|         if names and not any(n.tree_name is tree_name for n in names):
 | |
|             return names
 | |
| 
 | |
|         path = import_path + (from_import_name,)
 | |
|         importer = Importer(context.inference_state, path, module_context, level)
 | |
|         values = importer.follow()
 | |
|     return set(s.name for s in values)
 | |
| 
 | |
| 
 | |
| def _prepare_infer_import(module_context, tree_name):
 | |
|     import_node = search_ancestor(tree_name, 'import_name', 'import_from')
 | |
|     import_path = import_node.get_path_for_name(tree_name)
 | |
|     from_import_name = None
 | |
|     try:
 | |
|         from_names = import_node.get_from_names()
 | |
|     except AttributeError:
 | |
|         # Is an import_name
 | |
|         pass
 | |
|     else:
 | |
|         if len(from_names) + 1 == len(import_path):
 | |
|             # We have to fetch the from_names part first and then check
 | |
|             # if from_names exists in the modules.
 | |
|             from_import_name = import_path[-1]
 | |
|             import_path = from_names
 | |
| 
 | |
|     importer = Importer(module_context.inference_state, tuple(import_path),
 | |
|                         module_context, import_node.level)
 | |
| 
 | |
|     return from_import_name, tuple(import_path), import_node.level, importer.follow()
 | |
| 
 | |
| 
 | |
| def _add_error(value, name, message):
 | |
|     if hasattr(name, 'parent') and value is not None:
 | |
|         analysis.add(value, 'import-error', name, message)
 | |
|     else:
 | |
|         debug.warning('ImportError without origin: ' + message)
 | |
| 
 | |
| 
 | |
| def _level_to_base_import_path(project_path, directory, level):
 | |
|     """
 | |
|     In case the level is outside of the currently known package (something like
 | |
|     import .....foo), we can still try our best to help the user for
 | |
|     completions.
 | |
|     """
 | |
|     for i in range(level - 1):
 | |
|         old = directory
 | |
|         directory = os.path.dirname(directory)
 | |
|         if old == directory:
 | |
|             return None, None
 | |
| 
 | |
|     d = directory
 | |
|     level_import_paths = []
 | |
|     # Now that we are on the level that the user wants to be, calculate the
 | |
|     # import path for it.
 | |
|     while True:
 | |
|         if d == project_path:
 | |
|             return level_import_paths, d
 | |
|         dir_name = os.path.basename(d)
 | |
|         if dir_name:
 | |
|             level_import_paths.insert(0, dir_name)
 | |
|             d = os.path.dirname(d)
 | |
|         else:
 | |
|             return None, directory
 | |
| 
 | |
| 
 | |
| class Importer:
 | |
|     def __init__(self, inference_state, import_path, module_context, level=0):
 | |
|         """
 | |
|         An implementation similar to ``__import__``. Use `follow`
 | |
|         to actually follow the imports.
 | |
| 
 | |
|         *level* specifies whether to use absolute or relative imports. 0 (the
 | |
|         default) means only perform absolute imports. Positive values for level
 | |
|         indicate the number of parent directories to search relative to the
 | |
|         directory of the module calling ``__import__()`` (see PEP 328 for the
 | |
|         details).
 | |
| 
 | |
|         :param import_path: List of namespaces (strings or Names).
 | |
|         """
 | |
|         debug.speed('import %s %s' % (import_path, module_context))
 | |
|         self._inference_state = inference_state
 | |
|         self.level = level
 | |
|         self._module_context = module_context
 | |
| 
 | |
|         self._fixed_sys_path = None
 | |
|         self._infer_possible = True
 | |
|         if level:
 | |
|             base = module_context.get_value().py__package__()
 | |
|             # We need to care for two cases, the first one is if it's a valid
 | |
|             # Python import. This import has a properly defined module name
 | |
|             # chain like `foo.bar.baz` and an import in baz is made for
 | |
|             # `..lala.` It can then resolve to `foo.bar.lala`.
 | |
|             # The else here is a heuristic for all other cases, if for example
 | |
|             # in `foo` you search for `...bar`, it's obviously out of scope.
 | |
|             # However since Jedi tries to just do it's best, we help the user
 | |
|             # here, because he might have specified something wrong in his
 | |
|             # project.
 | |
|             if level <= len(base):
 | |
|                 # Here we basically rewrite the level to 0.
 | |
|                 base = tuple(base)
 | |
|                 if level > 1:
 | |
|                     base = base[:-level + 1]
 | |
|                 import_path = base + tuple(import_path)
 | |
|             else:
 | |
|                 path = module_context.py__file__()
 | |
|                 project_path = self._inference_state.project.path
 | |
|                 import_path = list(import_path)
 | |
|                 if path is None:
 | |
|                     # If no path is defined, our best guess is that the current
 | |
|                     # file is edited by a user on the current working
 | |
|                     # directory. We need to add an initial path, because it
 | |
|                     # will get removed as the name of the current file.
 | |
|                     directory = project_path
 | |
|                 else:
 | |
|                     directory = os.path.dirname(path)
 | |
| 
 | |
|                 base_import_path, base_directory = _level_to_base_import_path(
 | |
|                     project_path, directory, level,
 | |
|                 )
 | |
|                 if base_directory is None:
 | |
|                     # Everything is lost, the relative import does point
 | |
|                     # somewhere out of the filesystem.
 | |
|                     self._infer_possible = False
 | |
|                 else:
 | |
|                     self._fixed_sys_path = [base_directory]
 | |
| 
 | |
|                 if base_import_path is None:
 | |
|                     if import_path:
 | |
|                         _add_error(
 | |
|                             module_context, import_path[0],
 | |
|                             message='Attempted relative import beyond top-level package.'
 | |
|                         )
 | |
|                 else:
 | |
|                     import_path = base_import_path + import_path
 | |
|         self.import_path = import_path
 | |
| 
 | |
|     @property
 | |
|     def _str_import_path(self):
 | |
|         """Returns the import path as pure strings instead of `Name`."""
 | |
|         return tuple(
 | |
|             name.value if isinstance(name, tree.Name) else name
 | |
|             for name in self.import_path
 | |
|         )
 | |
| 
 | |
|     def _sys_path_with_modifications(self, is_completion):
 | |
|         if self._fixed_sys_path is not None:
 | |
|             return self._fixed_sys_path
 | |
| 
 | |
|         return (
 | |
|             # For import completions we don't want to see init paths, but for
 | |
|             # inference we want to show the user as much as possible.
 | |
|             # See GH #1446.
 | |
|             self._inference_state.get_sys_path(add_init_paths=not is_completion)
 | |
|             + [
 | |
|                 str(p) for p
 | |
|                 in sys_path.check_sys_path_modifications(self._module_context)
 | |
|             ]
 | |
|         )
 | |
| 
 | |
|     def follow(self):
 | |
|         if not self.import_path:
 | |
|             if self._fixed_sys_path:
 | |
|                 # This is a bit of a special case, that maybe should be
 | |
|                 # revisited. If the project path is wrong or the user uses
 | |
|                 # relative imports the wrong way, we might end up here, where
 | |
|                 # the `fixed_sys_path == project.path` in that case we kind of
 | |
|                 # use the project.path.parent directory as our path. This is
 | |
|                 # usually not a problem, except if imports in other places are
 | |
|                 # using the same names. Example:
 | |
|                 #
 | |
|                 # foo/                       < #1
 | |
|                 #   - setup.py
 | |
|                 #   - foo/                   < #2
 | |
|                 #     - __init__.py
 | |
|                 #     - foo.py               < #3
 | |
|                 #
 | |
|                 # If the top foo is our project folder and somebody uses
 | |
|                 # `from . import foo` in `setup.py`, it will resolve to foo #2,
 | |
|                 # which means that the import for foo.foo is cached as
 | |
|                 # `__init__.py` (#2) and not as `foo.py` (#3). This is usually
 | |
|                 # not an issue, because this case is probably pretty rare, but
 | |
|                 # might be an issue for some people.
 | |
|                 #
 | |
|                 # However for most normal cases where we work with different
 | |
|                 # file names, this code path hits where we basically change the
 | |
|                 # project path to an ancestor of project path.
 | |
|                 from jedi.inference.value.namespace import ImplicitNamespaceValue
 | |
|                 import_path = (os.path.basename(self._fixed_sys_path[0]),)
 | |
|                 ns = ImplicitNamespaceValue(
 | |
|                     self._inference_state,
 | |
|                     string_names=import_path,
 | |
|                     paths=self._fixed_sys_path,
 | |
|                 )
 | |
|                 return ValueSet({ns})
 | |
|             return NO_VALUES
 | |
|         if not self._infer_possible:
 | |
|             return NO_VALUES
 | |
| 
 | |
|         # Check caches first
 | |
|         from_cache = self._inference_state.stub_module_cache.get(self._str_import_path)
 | |
|         if from_cache is not None:
 | |
|             return ValueSet({from_cache})
 | |
|         from_cache = self._inference_state.module_cache.get(self._str_import_path)
 | |
|         if from_cache is not None:
 | |
|             return from_cache
 | |
| 
 | |
|         sys_path = self._sys_path_with_modifications(is_completion=False)
 | |
| 
 | |
|         return import_module_by_names(
 | |
|             self._inference_state, self.import_path, sys_path, self._module_context
 | |
|         )
 | |
| 
 | |
|     def _get_module_names(self, search_path=None, in_module=None):
 | |
|         """
 | |
|         Get the names of all modules in the search_path. This means file names
 | |
|         and not names defined in the files.
 | |
|         """
 | |
|         if search_path is None:
 | |
|             sys_path = self._sys_path_with_modifications(is_completion=True)
 | |
|         else:
 | |
|             sys_path = search_path
 | |
|         return list(iter_module_names(
 | |
|             self._inference_state, self._module_context, sys_path,
 | |
|             module_cls=ImportName if in_module is None else SubModuleName,
 | |
|             add_builtin_modules=search_path is None and in_module is None,
 | |
|         ))
 | |
| 
 | |
|     def completion_names(self, inference_state, only_modules=False):
 | |
|         """
 | |
|         :param only_modules: Indicates wheter it's possible to import a
 | |
|             definition that is not defined in a module.
 | |
|         """
 | |
|         if not self._infer_possible:
 | |
|             return []
 | |
| 
 | |
|         names = []
 | |
|         if self.import_path:
 | |
|             # flask
 | |
|             if self._str_import_path == ('flask', 'ext'):
 | |
|                 # List Flask extensions like ``flask_foo``
 | |
|                 for mod in self._get_module_names():
 | |
|                     modname = mod.string_name
 | |
|                     if modname.startswith('flask_'):
 | |
|                         extname = modname[len('flask_'):]
 | |
|                         names.append(ImportName(self._module_context, extname))
 | |
|                 # Now the old style: ``flaskext.foo``
 | |
|                 for dir in self._sys_path_with_modifications(is_completion=True):
 | |
|                     flaskext = os.path.join(dir, 'flaskext')
 | |
|                     if os.path.isdir(flaskext):
 | |
|                         names += self._get_module_names([flaskext])
 | |
| 
 | |
|             values = self.follow()
 | |
|             for value in values:
 | |
|                 # Non-modules are not completable.
 | |
|                 if value.api_type not in ('module', 'namespace'):  # not a module
 | |
|                     continue
 | |
|                 if not value.is_compiled():
 | |
|                     # sub_modules_dict is not implemented for compiled modules.
 | |
|                     names += value.sub_modules_dict().values()
 | |
| 
 | |
|             if not only_modules:
 | |
|                 from jedi.inference.gradual.conversion import convert_values
 | |
| 
 | |
|                 both_values = values | convert_values(values)
 | |
|                 for c in both_values:
 | |
|                     for filter in c.get_filters():
 | |
|                         names += filter.values()
 | |
|         else:
 | |
|             if self.level:
 | |
|                 # We only get here if the level cannot be properly calculated.
 | |
|                 names += self._get_module_names(self._fixed_sys_path)
 | |
|             else:
 | |
|                 # This is just the list of global imports.
 | |
|                 names += self._get_module_names()
 | |
|         return names
 | |
| 
 | |
| 
 | |
| def import_module_by_names(inference_state, import_names, sys_path=None,
 | |
|                            module_context=None, prefer_stubs=True):
 | |
|     if sys_path is None:
 | |
|         sys_path = inference_state.get_sys_path()
 | |
| 
 | |
|     str_import_names = tuple(
 | |
|         i.value if isinstance(i, tree.Name) else i
 | |
|         for i in import_names
 | |
|     )
 | |
|     value_set = [None]
 | |
|     for i, name in enumerate(import_names):
 | |
|         value_set = ValueSet.from_sets([
 | |
|             import_module(
 | |
|                 inference_state,
 | |
|                 str_import_names[:i+1],
 | |
|                 parent_module_value,
 | |
|                 sys_path,
 | |
|                 prefer_stubs=prefer_stubs,
 | |
|             ) for parent_module_value in value_set
 | |
|         ])
 | |
|         if not value_set:
 | |
|             message = 'No module named ' + '.'.join(str_import_names)
 | |
|             if module_context is not None:
 | |
|                 _add_error(module_context, name, message)
 | |
|             else:
 | |
|                 debug.warning(message)
 | |
|             return NO_VALUES
 | |
|     return value_set
 | |
| 
 | |
| 
 | |
| @plugin_manager.decorate()
 | |
| @import_module_decorator
 | |
| def import_module(inference_state, import_names, parent_module_value, sys_path):
 | |
|     """
 | |
|     This method is very similar to importlib's `_gcd_import`.
 | |
|     """
 | |
|     if import_names[0] in settings.auto_import_modules:
 | |
|         module = _load_builtin_module(inference_state, import_names, sys_path)
 | |
|         if module is None:
 | |
|             return NO_VALUES
 | |
|         return ValueSet([module])
 | |
| 
 | |
|     module_name = '.'.join(import_names)
 | |
|     if parent_module_value is None:
 | |
|         # Override the sys.path. It works only good that way.
 | |
|         # Injecting the path directly into `find_module` did not work.
 | |
|         file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
 | |
|             string=import_names[-1],
 | |
|             full_name=module_name,
 | |
|             sys_path=sys_path,
 | |
|             is_global_search=True,
 | |
|         )
 | |
|         if is_pkg is None:
 | |
|             return NO_VALUES
 | |
|     else:
 | |
|         paths = parent_module_value.py__path__()
 | |
|         if paths is None:
 | |
|             # The module might not be a package.
 | |
|             return NO_VALUES
 | |
| 
 | |
|         file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info(
 | |
|             string=import_names[-1],
 | |
|             path=paths,
 | |
|             full_name=module_name,
 | |
|             is_global_search=False,
 | |
|         )
 | |
|         if is_pkg is None:
 | |
|             return NO_VALUES
 | |
| 
 | |
|     if isinstance(file_io_or_ns, ImplicitNSInfo):
 | |
|         from jedi.inference.value.namespace import ImplicitNamespaceValue
 | |
|         module = ImplicitNamespaceValue(
 | |
|             inference_state,
 | |
|             string_names=tuple(file_io_or_ns.name.split('.')),
 | |
|             paths=file_io_or_ns.paths,
 | |
|         )
 | |
|     elif file_io_or_ns is None:
 | |
|         module = _load_builtin_module(inference_state, import_names, sys_path)
 | |
|         if module is None:
 | |
|             return NO_VALUES
 | |
|     else:
 | |
|         module = _load_python_module(
 | |
|             inference_state, file_io_or_ns,
 | |
|             import_names=import_names,
 | |
|             is_package=is_pkg,
 | |
|         )
 | |
| 
 | |
|     if parent_module_value is None:
 | |
|         debug.dbg('global search_module %s: %s', import_names[-1], module)
 | |
|     else:
 | |
|         debug.dbg('search_module %s in paths %s: %s', module_name, paths, module)
 | |
|     return ValueSet([module])
 | |
| 
 | |
| 
 | |
| def _load_python_module(inference_state, file_io,
 | |
|                         import_names=None, is_package=False):
 | |
|     module_node = inference_state.parse(
 | |
|         file_io=file_io,
 | |
|         cache=True,
 | |
|         diff_cache=settings.fast_parser,
 | |
|         cache_path=settings.cache_directory,
 | |
|     )
 | |
| 
 | |
|     from jedi.inference.value import ModuleValue
 | |
|     return ModuleValue(
 | |
|         inference_state, module_node,
 | |
|         file_io=file_io,
 | |
|         string_names=import_names,
 | |
|         code_lines=get_cached_code_lines(inference_state.grammar, file_io.path),
 | |
|         is_package=is_package,
 | |
|     )
 | |
| 
 | |
| 
 | |
| def _load_builtin_module(inference_state, import_names=None, sys_path=None):
 | |
|     project = inference_state.project
 | |
|     if sys_path is None:
 | |
|         sys_path = inference_state.get_sys_path()
 | |
|     if not project._load_unsafe_extensions:
 | |
|         safe_paths = project._get_base_sys_path(inference_state)
 | |
|         sys_path = [p for p in sys_path if p in safe_paths]
 | |
| 
 | |
|     dotted_name = '.'.join(import_names)
 | |
|     assert dotted_name is not None
 | |
|     module = compiled.load_module(inference_state, dotted_name=dotted_name, sys_path=sys_path)
 | |
|     if module is None:
 | |
|         # The file might raise an ImportError e.g. and therefore not be
 | |
|         # importable.
 | |
|         return None
 | |
|     return module
 | |
| 
 | |
| 
 | |
| def load_module_from_path(inference_state, file_io, import_names=None, is_package=None):
 | |
|     """
 | |
|     This should pretty much only be used for get_modules_containing_name. It's
 | |
|     here to ensure that a random path is still properly loaded into the Jedi
 | |
|     module structure.
 | |
|     """
 | |
|     path = Path(file_io.path)
 | |
|     if import_names is None:
 | |
|         e_sys_path = inference_state.get_sys_path()
 | |
|         import_names, is_package = sys_path.transform_path_to_dotted(e_sys_path, path)
 | |
|     else:
 | |
|         assert isinstance(is_package, bool)
 | |
| 
 | |
|     is_stub = path.suffix == '.pyi'
 | |
|     if is_stub:
 | |
|         folder_io = file_io.get_parent_folder()
 | |
|         if folder_io.path.endswith('-stubs'):
 | |
|             folder_io = FolderIO(folder_io.path[:-6])
 | |
|         if path.name == '__init__.pyi':
 | |
|             python_file_io = folder_io.get_file_io('__init__.py')
 | |
|         else:
 | |
|             python_file_io = folder_io.get_file_io(import_names[-1] + '.py')
 | |
| 
 | |
|         try:
 | |
|             v = load_module_from_path(
 | |
|                 inference_state, python_file_io,
 | |
|                 import_names, is_package=is_package
 | |
|             )
 | |
|             values = ValueSet([v])
 | |
|         except FileNotFoundError:
 | |
|             values = NO_VALUES
 | |
| 
 | |
|         return create_stub_module(
 | |
|             inference_state, inference_state.latest_grammar, values,
 | |
|             parse_stub_module(inference_state, file_io), file_io, import_names
 | |
|         )
 | |
|     else:
 | |
|         module = _load_python_module(
 | |
|             inference_state, file_io,
 | |
|             import_names=import_names,
 | |
|             is_package=is_package,
 | |
|         )
 | |
|         inference_state.module_cache.add(import_names, ValueSet([module]))
 | |
|         return module
 | |
| 
 | |
| 
 | |
| def load_namespace_from_path(inference_state, folder_io):
 | |
|     import_names, is_package = sys_path.transform_path_to_dotted(
 | |
|         inference_state.get_sys_path(),
 | |
|         Path(folder_io.path)
 | |
|     )
 | |
|     from jedi.inference.value.namespace import ImplicitNamespaceValue
 | |
|     return ImplicitNamespaceValue(inference_state, import_names, [folder_io.path])
 | |
| 
 | |
| 
 | |
| def follow_error_node_imports_if_possible(context, name):
 | |
|     error_node = tree.search_ancestor(name, 'error_node')
 | |
|     if error_node is not None:
 | |
|         # Get the first command start of a started simple_stmt. The error
 | |
|         # node is sometimes a small_stmt and sometimes a simple_stmt. Check
 | |
|         # for ; leaves that start a new statements.
 | |
|         start_index = 0
 | |
|         for index, n in enumerate(error_node.children):
 | |
|             if n.start_pos > name.start_pos:
 | |
|                 break
 | |
|             if n == ';':
 | |
|                 start_index = index + 1
 | |
|         nodes = error_node.children[start_index:]
 | |
|         first_name = nodes[0].get_first_leaf().value
 | |
| 
 | |
|         # Make it possible to infer stuff like `import foo.` or
 | |
|         # `from foo.bar`.
 | |
|         if first_name in ('from', 'import'):
 | |
|             is_import_from = first_name == 'from'
 | |
|             level, names = helpers.parse_dotted_names(
 | |
|                 nodes,
 | |
|                 is_import_from=is_import_from,
 | |
|                 until_node=name,
 | |
|             )
 | |
|             return Importer(
 | |
|                 context.inference_state, names, context.get_root_context(), level).follow()
 | |
|     return None
 | |
| 
 | |
| 
 | |
| def iter_module_names(inference_state, module_context, search_path,
 | |
|                       module_cls=ImportName, add_builtin_modules=True):
 | |
|     """
 | |
|     Get the names of all modules in the search_path. This means file names
 | |
|     and not names defined in the files.
 | |
|     """
 | |
|     # add builtin module names
 | |
|     if add_builtin_modules:
 | |
|         for name in inference_state.compiled_subprocess.get_builtin_module_names():
 | |
|             yield module_cls(module_context, name)
 | |
| 
 | |
|     for name in inference_state.compiled_subprocess.iter_module_names(search_path):
 | |
|         yield module_cls(module_context, name)
 |