Skip to content

Partially remove dependency on imp. #686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion astroid/brain/brain_six.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def prefixed_lines():
from sys import intern
map = map
range = range
from imp import reload as reload_module
from importlib import reload as reload_module
from functools import reduce
from shlex import quote as shlex_quote
from io import StringIO
Expand Down
89 changes: 54 additions & 35 deletions astroid/interpreter/_import/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,13 @@
import collections
import distutils
import enum
import imp
import os
import sys
import zipimport

try:
import importlib.machinery
import importlib.machinery

_HAS_MACHINERY = True
except ImportError:
_HAS_MACHINERY = False
_HAS_MACHINERY = True

try:
from functools import lru_cache
Expand All @@ -35,22 +31,6 @@
"PY_CODERESOURCE PY_COMPILED PY_FROZEN PY_RESOURCE "
"PY_SOURCE PY_ZIPMODULE PY_NAMESPACE",
)
_ImpTypes = {
imp.C_BUILTIN: ModuleType.C_BUILTIN,
imp.C_EXTENSION: ModuleType.C_EXTENSION,
imp.PKG_DIRECTORY: ModuleType.PKG_DIRECTORY,
imp.PY_COMPILED: ModuleType.PY_COMPILED,
imp.PY_FROZEN: ModuleType.PY_FROZEN,
imp.PY_SOURCE: ModuleType.PY_SOURCE,
}
if hasattr(imp, "PY_RESOURCE"):
_ImpTypes[imp.PY_RESOURCE] = ModuleType.PY_RESOURCE
if hasattr(imp, "PY_CODERESOURCE"):
_ImpTypes[imp.PY_CODERESOURCE] = ModuleType.PY_CODERESOURCE


def _imp_type_to_module_type(imp_type):
return _ImpTypes[imp_type]


_ModuleSpec = collections.namedtuple(
Expand Down Expand Up @@ -116,23 +96,62 @@ class ImpFinder(Finder):
"""A finder based on the imp module."""

def find_module(self, modname, module_parts, processed, submodule_path):
if not isinstance(modname, str):
raise TypeError("'modname' must be a str, not {}".format(type(modname)))
if submodule_path is not None:
submodule_path = list(submodule_path)
try:
stream, mp_filename, mp_desc = imp.find_module(modname, submodule_path)
except ImportError:
return None

# Close resources.
if stream:
stream.close()

return ModuleSpec(
name=modname,
location=mp_filename,
module_type=_imp_type_to_module_type(mp_desc[2]),
else:
try:
loader = importlib.util.find_spec(modname)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious. The function returns ModuleSpec object, why did you name it loader?

if loader:
if loader.loader is importlib.machinery.BuiltinImporter:
return ModuleSpec(
name=modname,
location=None,
module_type=ModuleType.C_BUILTIN,
)
if loader.loader is importlib.machinery.FrozenImporter:
return ModuleSpec(
name=modname,
location=None,
module_type=ModuleType.PY_FROZEN,
)
except ValueError:
pass
submodule_path = sys.path

suffixes = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to build this list globally instead of building it every time this function is called?

[
(s, ModuleType.C_EXTENSION)
for s in importlib.machinery.EXTENSION_SUFFIXES
]
+ [(s, ModuleType.PY_SOURCE) for s in importlib.machinery.SOURCE_SUFFIXES]
+ [
(s, ModuleType.PY_COMPILED)
for s in importlib.machinery.BYTECODE_SUFFIXES
]
)

for entry in submodule_path:
package_directory = os.path.join(entry, modname)
for suffix in [".py", importlib.machinery.BYTECODE_SUFFIXES[0]]:
package_file_name = "__init__" + suffix
file_path = os.path.join(package_directory, package_file_name)
if os.path.isfile(file_path):
return ModuleSpec(
name=modname,
location=package_directory,
module_type=ModuleType.PKG_DIRECTORY,
)
for suffix, type_ in suffixes:
file_name = modname + suffix
file_path = os.path.join(entry, file_name)
if os.path.isfile(file_path):
return ModuleSpec(
name=modname, location=file_path, module_type=type_
)
return None

def contribute_to_path(self, spec, processed):
if spec.location is None:
# Builtin.
Expand Down
27 changes: 18 additions & 9 deletions astroid/modutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
:var BUILTIN_MODULES: dictionary with builtin module names has key
"""
import imp
import importlib.util
import os
import platform
import sys
Expand Down Expand Up @@ -426,7 +427,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)

Expand Down Expand Up @@ -622,16 +625,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 #####################################################

Expand Down
1 change: 1 addition & 0 deletions astroid/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,7 @@ def extra_decorators(self):
decorators.append(assign.value)
return decorators

# pylint: disable=invalid-overridden-method
@decorators_mod.cachedproperty
def type(self): # pylint: disable=invalid-overridden-method
"""The function type for this node.
Expand Down
4 changes: 1 addition & 3 deletions astroid/tests/testdata/python2/data/nonregr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def toto(value):
print(v.get('yo'))


import imp
fp, mpath, desc = imp.find_module('optparse',a)
s_opt = imp.load_module('std_optparse', fp, mpath, desc)
import optparse as s_opt

class OptionParser(s_opt.OptionParser):

Expand Down
4 changes: 1 addition & 3 deletions astroid/tests/testdata/python3/data/nonregr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def toto(value):
print(v.get('yo'))


import imp
fp, mpath, desc = imp.find_module('optparse',a)
s_opt = imp.load_module('std_optparse', fp, mpath, desc)
import optparse as s_opt

class OptionParser(s_opt.OptionParser):

Expand Down
6 changes: 4 additions & 2 deletions astroid/tests/unittest_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,10 @@ def test_multiprocessing_manager(self):
obj = next(module[attr].infer())
self.assertEqual(obj.qname(), "{}.{}".format(bases.BUILTINS, attr))

array = next(module["array"].infer())
self.assertEqual(array.qname(), "array.array")
# pypy's implementation of array.__spec__ return None. This causes problems for this inference.
if not hasattr(sys, "pypy_version_info"):
array = next(module["array"].infer())
self.assertEqual(array.qname(), "array.array")

manager = next(module["manager"].infer())
# Verify that we have these attributes
Expand Down
17 changes: 17 additions & 0 deletions astroid/tests/unittest_modutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,23 @@ def test_knownValues_is_relative_1(self):
def test_knownValues_is_relative_3(self):
self.assertFalse(modutils.is_relative("astroid", astroid.__path__[0]))

def test_deep_relative(self):
self.assertTrue(modutils.is_relative("ElementTree", xml.etree.__path__[0]))

def test_deep_relative2(self):
self.assertFalse(modutils.is_relative("ElementTree", xml.__path__[0]))

def test_deep_relative3(self):
self.assertTrue(modutils.is_relative("etree.ElementTree", xml.__path__[0]))

def test_deep_relative4(self):
self.assertTrue(modutils.is_relative("etree.gibberish", xml.__path__[0]))

def test_is_relative_bad_path(self):
self.assertFalse(
modutils.is_relative("ElementTree", os.path.join(xml.__path__[0], "ftree"))
)


class GetModuleFilesTest(unittest.TestCase):
def test_get_module_files_1(self):
Expand Down