[PATCH 6 of 6] extensions: imp module is removed in Python 3.12 - use importlib to load files

Mads Kiilerich mads at kiilerich.com
Wed Jun 28 14:43:41 UTC 2023


# HG changeset patch
# User Mads Kiilerich <mads at kiilerich.com>
# Date 1687954993 -7200
#      Wed Jun 28 14:23:13 2023 +0200
# Branch stable
# Node ID 24d8509e44785f94047efebbcbb38986323913cd
# Parent  f45267a4d61f7687727efa7fe571d4f2522a3bb6
extensions: imp module is removed in Python 3.12 - use importlib to load files

imp has been deprecated for a long time, and has finally been removed in Python
3.12 .

imp was only used for loading extensions that has been specified with direct
.py path or path to a package directory. The same use cases can be achieved
quite simple with importlib, , possiby with small changes in corner cases with
undefined behaviour, such as extensions without .py source.

There might also be corner cases and undefined behaviour around use of
sys.modules and reloading.

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -9,9 +9,10 @@
 import ast
 import collections
 import functools
-import imp
+import importlib
 import inspect
 import os
+import sys
 
 from .i18n import (
     _,
@@ -89,20 +90,17 @@ def loadpath(path, module_name):
     path = pycompat.fsdecode(path)
     if os.path.isdir(path):
         # module/__init__.py style
-        d, f = os.path.split(path)
-        fd, fpath, desc = imp.find_module(f, [d])
-        # When https://github.com/python/typeshed/issues/3466 is fixed
-        # and in a pytype release we can drop this disable.
-        return imp.load_module(
-            module_name, fd, fpath, desc  # pytype: disable=wrong-arg-types
-        )
-    else:
-        try:
-            return imp.load_source(module_name, path)
-        except IOError as exc:
-            if not exc.filename:
-                exc.filename = path  # python does not fill this
-            raise
+        init_py_path = os.path.join(path, '__init__.py')
+        if not os.path.exists(init_py_path):
+            raise ImportError("No module named '%s'" % os.path.basename(path))
+        path = init_py_path
+
+    loader = importlib.machinery.SourceFileLoader(module_name, path)
+    spec = importlib.util.spec_from_file_location(module_name, loader=loader)
+    module = importlib.util.module_from_spec(spec)
+    sys.modules[module_name] = module
+    spec.loader.exec_module(module)
+    return module
 
 
 def _importh(name):



More information about the Mercurial-devel mailing list