Skip to content

Commit 954753b

Browse files
committed
replace imp module with importlib
When trying to run cloudbase-init using Python 3.12, it errors out ModuleNotFoundError: No module named 'imp'. The 'imp' module was replaced with similar functionality from module importlib. These two implementations are equivalent: ```python import imp import os import site wmi_path = None for packages_path in site.getsitepackages(): path = os.path.join(packages_path, "wmi.py") if os.path.isfile(path): wmi_path = path break wmi_module_name = "wmi" wmi_module = imp.load_source(wmi_module_name, wmi_path) ``` ```python import importlib.util import os import site wmi_path = None for packages_path in site.getsitepackages(): path = os.path.join(packages_path, "wmi.py") if os.path.isfile(path): wmi_path = path break wmi_module_name = "wmi" wmi_module_spec = importlib.util.spec_from_file_location(wmi_module_name, wmi_path) wmi_module = importlib.util.module_from_spec(wmi_module_spec) wmi_module_spec.loader.exec_module(wmi_module) ``` Fixes: #139 Change-Id: I6490c6d9922efea26ab8d167a0d6e41ce34d6c2c Signed-off-by: Adrian Vladu <avladu@cloudbasesolutions.com>
1 parent 6c1dc5b commit 954753b

4 files changed

Lines changed: 21 additions & 15 deletions

File tree

cloudbaseinit/tests/utils/test_classloader.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ class ClassLoaderTest(unittest.TestCase):
3535
def setUp(self):
3636
self._loader = classloader.ClassLoader()
3737

38-
@mock.patch('imp.load_compiled')
39-
@mock.patch('imp.load_source')
40-
def test_load_module_py(self, mock_source, mock_compiled):
38+
@mock.patch('cloudbaseinit.utils.classloader.load_module_from_path')
39+
def test_load_module_py(self, mock_source_compiled):
4140
mock_py = os.path.join(_create_tempfile(), "mock.py")
4241
mock_pyc = os.path.join(_create_tempfile(), "mock.pyc")
43-
mock_source.return_value = mock_compiled.return_value = None
42+
mock_source_compiled.return_value = None
4443
result_module_py = self._loader.load_module(mock_py)
4544
result_module_pyc = self._loader.load_module(mock_pyc)
4645
self.assertIsNone(result_module_py)

cloudbaseinit/tests/utils/windows/test_wmi_loader.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,24 @@ def test_load_pymi(self):
3232
wmi_loader = importlib.import_module(MODPATH)
3333
self.assertEqual(mock.sentinel.wmi, wmi_loader.wmi())
3434

35-
@mock.patch('imp.load_source')
35+
@mock.patch('cloudbaseinit.utils.classloader.load_module_from_path')
3636
@mock.patch('os.path.isfile')
37-
def test_load_legacy_wmi(self, mock_isfile, mock_load_source):
37+
def test_load_legacy_wmi(self, mock_isfile, mock_load_module_from_path):
3838
mock_isfile.return_value = True
3939

4040
mock_site = mock.MagicMock()
4141
fake_site_path = "fake_site_path"
4242
mock_site.getsitepackages.return_value = [fake_site_path]
43-
mock_load_source.return_value = mock.sentinel.wmi
43+
mock_load_module_from_path.return_value = mock.sentinel.wmi
4444

4545
with mock.patch.dict('sys.modules', {'wmi': None, 'site': mock_site}):
4646
wmi_loader = importlib.import_module(MODPATH)
4747
self.assertEqual(mock.sentinel.wmi, wmi_loader.wmi())
4848

4949
fake_wmi_path = os.path.join(fake_site_path, "wmi.py")
5050
mock_isfile.assert_called_once_with(fake_wmi_path)
51-
mock_load_source.assert_called_once_with("wmi", fake_wmi_path)
51+
mock_load_module_from_path.assert_called_once_with(
52+
"wmi", fake_wmi_path)
5253

5354
@mock.patch('os.path.isfile')
5455
def test_load_legacy_wmi_fail(self, mock_isfile):

cloudbaseinit/utils/classloader.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15-
import imp
15+
import importlib
1616
import os
1717

1818
from oslo_log import log as oslo_logging
@@ -21,6 +21,14 @@
2121
LOG = oslo_logging.getLogger(__name__)
2222

2323

24+
def load_module_from_path(module_name, path):
25+
spec = importlib.util.spec_from_file_location(module_name, path)
26+
module = importlib.util.module_from_spec(spec)
27+
spec.loader.exec_module(module)
28+
29+
return module
30+
31+
2432
class ClassLoader(object):
2533

2634
def load_class(self, class_path):
@@ -32,9 +40,7 @@ def load_class(self, class_path):
3240
def load_module(self, path):
3341
module_name, file_ext = os.path.splitext(os.path.split(path)[-1])
3442

35-
if file_ext.lower() == '.py':
36-
module = imp.load_source(module_name, path)
37-
elif file_ext.lower() == '.pyc':
38-
module = imp.load_compiled(module_name, path)
43+
if file_ext.lower() in ['.py', '.pyc']:
44+
module = load_module_from_path(module_name, path)
3945

4046
return module

cloudbaseinit/utils/windows/wmi_loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15-
import imp
1615
import os
1716
import site
1817

1918
from oslo_log import log as oslo_logging
2019

2120
from cloudbaseinit import exception
21+
from cloudbaseinit.utils.classloader import load_module_from_path
2222

2323
LOG = oslo_logging.getLogger(__name__)
2424

@@ -41,4 +41,4 @@ def wmi():
4141
if wmi_path is None:
4242
raise exception.ItemNotFoundException("wmi module not found")
4343

44-
return imp.load_source("wmi", wmi_path)
44+
return load_module_from_path("wmi", wmi_path)

0 commit comments

Comments
 (0)