diff options
Diffstat (limited to 'astroid/modutils.py')
-rw-r--r-- | astroid/modutils.py | 124 |
1 files changed, 44 insertions, 80 deletions
diff --git a/astroid/modutils.py b/astroid/modutils.py index 4e6ed86b..4ec8ce95 100644 --- a/astroid/modutils.py +++ b/astroid/modutils.py @@ -16,6 +16,8 @@ # Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com> # Copyright (c) 2019 markmcclain <markmcclain@users.noreply.github.com> # Copyright (c) 2019 BasPH <BasPH@users.noreply.github.com> +# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com> +# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com> # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER @@ -31,7 +33,7 @@ :type BUILTIN_MODULES: dict :var BUILTIN_MODULES: dictionary with builtin module names has key """ -import imp +import importlib.util import os import platform import sys @@ -44,7 +46,6 @@ from distutils.errors import DistutilsPlatformError # distutils is replaced by virtualenv with a module that does # weird path manipulations in order to get to the # real distutils module. -from typing import Optional, List from .interpreter._import import spec from .interpreter._import import util @@ -90,11 +91,23 @@ if os.name == "nt": pass if platform.python_implementation() == "PyPy": + # The get_python_lib(standard_lib=True) function does not give valid + # result with pypy in a virtualenv. + # In a virtual environment, with CPython implementation the call to this function returns a path toward + # the binary (its libraries) which has been used to create the virtual environment. + # Not with pypy implementation. + # The only way to retrieve such information is to use the sys.base_prefix hint. + # It's worth noticing that under CPython implementation the return values of + # get_python_lib(standard_lib=True) and get_python_lib(santdard_lib=True, prefix=sys.base_prefix) + # are the same. + # In the lines above, we could have replace the call to get_python_lib(standard=True) + # with the one using prefix=sys.base_prefix but we prefer modifying only what deals with pypy. + STD_LIB_DIRS.add(get_python_lib(standard_lib=True, prefix=sys.base_prefix)) _root = os.path.join(sys.prefix, "lib_pypy") STD_LIB_DIRS.add(_root) try: # real_prefix is defined when running inside virtualenv. - STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "lib_pypy")) + STD_LIB_DIRS.add(os.path.join(sys.base_prefix, "lib_pypy")) except AttributeError: pass del _root @@ -178,110 +191,53 @@ def _cache_normalize_path(path): return result -def load_module_from_name(dotted_name, path=None, use_sys=True): +def load_module_from_name(dotted_name): """Load a Python module from its name. :type dotted_name: str :param dotted_name: python name of a module or package - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - :raise ImportError: if the module or package is not found :rtype: module :return: the loaded module """ - return load_module_from_modpath(dotted_name.split("."), path, use_sys) + try: + return sys.modules[dotted_name] + except KeyError: + pass + return importlib.import_module(dotted_name) -def load_module_from_modpath(parts, path: Optional[List[str]] = None, use_sys=1): + +def load_module_from_modpath(parts): """Load a python module from its split name. :type parts: list(str) or tuple(str) :param parts: python name of a module or package split on '.' - :param path: - Optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - :raise ImportError: if the module or package is not found :rtype: module :return: the loaded module """ - if use_sys: - try: - return sys.modules[".".join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = ".".join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = imp.find_module(part, path) - module = imp.load_module(curname, mp_file, mp_filename, mp_desc) - # mp_file still needs to be closed. - if mp_file: - mp_file.close() - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, "__file__", "") - prevmodule = module - if not _file and util.is_namespace(curname): - continue - if not _file and len(modpath) != len(parts): - raise ImportError("no module in %s" % ".".join(parts[len(modpath) :])) - path = [os.path.dirname(_file)] - return module + return load_module_from_name(".".join(parts)) -def load_module_from_file( - filepath: str, path: Optional[List[str]] = None, use_sys=True -): +def load_module_from_file(filepath: str): """Load a Python module from it's path. :type filepath: str :param filepath: path to the python module or package - :param Optional[List[str]] path: - Optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - :raise ImportError: if the module or package is not found :rtype: module :return: the loaded module """ modpath = modpath_from_file(filepath) - return load_module_from_modpath(modpath, path, use_sys) + return load_module_from_modpath(modpath) def check_modpath_has_init(path, mod_path): @@ -418,7 +374,9 @@ def file_info_from_modpath(modpath, path=None, context_file=None): elif modpath == ["os", "path"]: # FIXME: currently ignoring search_path... return spec.ModuleSpec( - name="os.path", location=os.path.__file__, module_type=imp.PY_SOURCE + name="os.path", + location=os.path.__file__, + module_type=spec.ModuleType.PY_SOURCE, ) return _spec_from_modpath(modpath, path, context) @@ -614,16 +572,22 @@ def is_relative(modname, from_file): from_file = os.path.dirname(from_file) if from_file in sys.path: return False - try: - stream, _, _ = imp.find_module(modname.split(".")[0], [from_file]) - - # Close the stream to avoid ResourceWarnings. - if stream: - stream.close() - return True - except ImportError: + name = os.path.basename(from_file) + file_path = os.path.dirname(from_file) + parent_spec = importlib.util.find_spec(name, from_file) + while parent_spec is None and len(file_path) > 0: + name = os.path.basename(file_path) + "." + name + file_path = os.path.dirname(file_path) + parent_spec = importlib.util.find_spec(name, from_file) + + if parent_spec is None: return False + submodule_spec = importlib.util.find_spec( + name + "." + modname.split(".")[0], parent_spec.submodule_search_locations + ) + return submodule_spec is not None + # internal only functions ##################################################### |