Skip to content

Commit

Permalink
replace imp module with importlib
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ader1990 committed May 30, 2024
1 parent 6c1dc5b commit 954753b
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 15 deletions.
7 changes: 3 additions & 4 deletions cloudbaseinit/tests/utils/test_classloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@ class ClassLoaderTest(unittest.TestCase):
def setUp(self):
self._loader = classloader.ClassLoader()

@mock.patch('imp.load_compiled')
@mock.patch('imp.load_source')
def test_load_module_py(self, mock_source, mock_compiled):
@mock.patch('cloudbaseinit.utils.classloader.load_module_from_path')
def test_load_module_py(self, mock_source_compiled):
mock_py = os.path.join(_create_tempfile(), "mock.py")
mock_pyc = os.path.join(_create_tempfile(), "mock.pyc")
mock_source.return_value = mock_compiled.return_value = None
mock_source_compiled.return_value = None
result_module_py = self._loader.load_module(mock_py)
result_module_pyc = self._loader.load_module(mock_pyc)
self.assertIsNone(result_module_py)
Expand Down
9 changes: 5 additions & 4 deletions cloudbaseinit/tests/utils/windows/test_wmi_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,24 @@ def test_load_pymi(self):
wmi_loader = importlib.import_module(MODPATH)
self.assertEqual(mock.sentinel.wmi, wmi_loader.wmi())

@mock.patch('imp.load_source')
@mock.patch('cloudbaseinit.utils.classloader.load_module_from_path')
@mock.patch('os.path.isfile')
def test_load_legacy_wmi(self, mock_isfile, mock_load_source):
def test_load_legacy_wmi(self, mock_isfile, mock_load_module_from_path):
mock_isfile.return_value = True

mock_site = mock.MagicMock()
fake_site_path = "fake_site_path"
mock_site.getsitepackages.return_value = [fake_site_path]
mock_load_source.return_value = mock.sentinel.wmi
mock_load_module_from_path.return_value = mock.sentinel.wmi

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

fake_wmi_path = os.path.join(fake_site_path, "wmi.py")
mock_isfile.assert_called_once_with(fake_wmi_path)
mock_load_source.assert_called_once_with("wmi", fake_wmi_path)
mock_load_module_from_path.assert_called_once_with(
"wmi", fake_wmi_path)

@mock.patch('os.path.isfile')
def test_load_legacy_wmi_fail(self, mock_isfile):
Expand Down
16 changes: 11 additions & 5 deletions cloudbaseinit/utils/classloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.

import imp
import importlib
import os

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


def load_module_from_path(module_name, path):
spec = importlib.util.spec_from_file_location(module_name, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

return module


class ClassLoader(object):

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

if file_ext.lower() == '.py':
module = imp.load_source(module_name, path)
elif file_ext.lower() == '.pyc':
module = imp.load_compiled(module_name, path)
if file_ext.lower() in ['.py', '.pyc']:
module = load_module_from_path(module_name, path)

return module
4 changes: 2 additions & 2 deletions cloudbaseinit/utils/windows/wmi_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.

import imp
import os
import site

from oslo_log import log as oslo_logging

from cloudbaseinit import exception
from cloudbaseinit.utils.classloader import load_module_from_path

LOG = oslo_logging.getLogger(__name__)

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

return imp.load_source("wmi", wmi_path)
return load_module_from_path("wmi", wmi_path)

0 comments on commit 954753b

Please sign in to comment.