https://github.com/python/cpython/commit/2fd43a1ffe4ff1f6c46f6045bc327d6085c40fbf
commit: 2fd43a1ffe4ff1f6c46f6045bc327d6085c40fbf
branch: main
author: Lisa Roach <[email protected]>
committer: lisroach <[email protected]>
date: 2025-09-19T06:21:42-07:00
summary:

gh-138310: Adds sys.audit event for import_module (#138311)

* Updates sys.audit calls for imports to include import_module
* Adds unit tests for existing and new functionality

files:
A Lib/test/audit_test_data/__init__.py
A Lib/test/audit_test_data/submodule.py
A Lib/test/audit_test_data/submodule2.py
M Lib/importlib/_bootstrap.py
M Lib/test/audit-tests.py
M Lib/test/test_audit.py
M Makefile.pre.in
M Python/import.c

diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 499da1e04efea8..43c66765dd9779 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -1307,6 +1307,14 @@ def _sanity_check(name, package, level):
 
 def _find_and_load_unlocked(name, import_):
     path = None
+    sys.audit(
+        "import",
+        name,
+        path,
+        getattr(sys, "path", None),
+        getattr(sys, "meta_path", None),
+        getattr(sys, "path_hooks", None)
+    )
     parent = name.rpartition('.')[0]
     parent_spec = None
     if parent:
diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py
index 6884ac0dbe6ff0..a893932169a089 100644
--- a/Lib/test/audit-tests.py
+++ b/Lib/test/audit-tests.py
@@ -8,6 +8,8 @@
 import contextlib
 import os
 import sys
+import unittest.mock
+from test.support import swap_item
 
 
 class TestHook:
@@ -672,6 +674,84 @@ def hook(event, args):
         assertEqual(event_script_path, tmp_file.name)
         assertEqual(remote_event_script_path, tmp_file.name)
 
+def test_import_module():
+    import importlib
+
+    with TestHook() as hook:
+        importlib.import_module("importlib")  # already imported, won't get 
logged
+        importlib.import_module("email") # standard library module
+        importlib.import_module("pythoninfo")  # random module
+        importlib.import_module(".audit_test_data.submodule", "test")  # 
relative import
+        importlib.import_module("test.audit_test_data.submodule2")  # absolute 
import
+        importlib.import_module("_testcapi")  # extension module
+
+    actual = [a for e, a in hook.seen if e == "import"]
+    assertSequenceEqual(
+        [
+            ("email", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("test.audit_test_data.submodule", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("test.audit_test_data", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("_testcapi", unittest.mock.ANY, None, None, None)
+        ],
+        actual,
+    )
+
+def test_builtin__import__():
+    import importlib # noqa: F401
+
+    with TestHook() as hook:
+        __import__("importlib")
+        __import__("email")
+        __import__("pythoninfo")
+        __import__("audit_test_data.submodule", level=1, 
globals={"__package__": "test"})
+        __import__("test.audit_test_data.submodule2")
+        __import__("_testcapi")
+
+    actual = [a for e, a in hook.seen if e == "import"]
+    assertSequenceEqual(
+        [
+            ("email", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("test.audit_test_data.submodule", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("test.audit_test_data", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("_testcapi", unittest.mock.ANY, None, None, None)
+        ],
+        actual,
+    )
+
+def test_import_statement():
+    import importlib # noqa: F401
+    # Set __package__ so relative imports work
+    with swap_item(globals(), "__package__", "test"):
+        with TestHook() as hook:
+            import importlib # noqa: F401
+            import email # noqa: F401
+            import pythoninfo # noqa: F401
+            from .audit_test_data import submodule # noqa: F401
+            import test.audit_test_data.submodule2 # noqa: F401
+            import _testcapi # noqa: F401
+
+    actual = [a for e, a in hook.seen if e == "import"]
+    # Import statement ordering is different because the package is
+    # loaded first and then the submodule
+    assertSequenceEqual(
+        [
+            ("email", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("test.audit_test_data", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("test.audit_test_data.submodule", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, 
sys.path_hooks),
+            ("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
+            ("_testcapi", unittest.mock.ANY, None, None, None)
+        ],
+        actual,
+    )
+
 if __name__ == "__main__":
     from test.support import suppress_msvcrt_asserts
 
diff --git a/Lib/test/audit_test_data/__init__.py 
b/Lib/test/audit_test_data/__init__.py
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/Lib/test/audit_test_data/submodule.py 
b/Lib/test/audit_test_data/submodule.py
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/Lib/test/audit_test_data/submodule2.py 
b/Lib/test/audit_test_data/submodule2.py
new file mode 100644
index 00000000000000..e69de29bb2d1d6
diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py
index 077765fcda210a..db4e1eb9999c1f 100644
--- a/Lib/test/test_audit.py
+++ b/Lib/test/test_audit.py
@@ -331,5 +331,14 @@ def test_sys_remote_exec(self):
         if returncode:
             self.fail(stderr)
 
+    def test_import_module(self):
+        self.do_test("test_import_module")
+
+    def test_builtin__import__(self):
+        self.do_test("test_builtin__import__")
+
+    def test_import_statement(self):
+        self.do_test("test_import_statement")
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 9f00ca1c0d541e..da036b198d11f8 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2594,6 +2594,7 @@ TESTSUBDIRS=      idlelib/idle_test \
                test/test_ast \
                test/test_ast/data \
                test/archivetestdata \
+               test/audit_test_data \
                test/audiodata \
                test/certdata \
                test/certdata/capath \
diff --git a/Python/import.c b/Python/import.c
index 9dee20ecb63c91..d01c4d478283ff 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -3681,33 +3681,6 @@ import_find_and_load(PyThreadState *tstate, PyObject 
*abs_name)
 
     PyTime_t t1 = 0, accumulated_copy = accumulated;
 
-    PyObject *sys_path, *sys_meta_path, *sys_path_hooks;
-    if (PySys_GetOptionalAttrString("path", &sys_path) < 0) {
-        return NULL;
-    }
-    if (PySys_GetOptionalAttrString("meta_path", &sys_meta_path) < 0) {
-        Py_XDECREF(sys_path);
-        return NULL;
-    }
-    if (PySys_GetOptionalAttrString("path_hooks", &sys_path_hooks) < 0) {
-        Py_XDECREF(sys_meta_path);
-        Py_XDECREF(sys_path);
-        return NULL;
-    }
-    if (_PySys_Audit(tstate, "import", "OOOOO",
-                     abs_name, Py_None, sys_path ? sys_path : Py_None,
-                     sys_meta_path ? sys_meta_path : Py_None,
-                     sys_path_hooks ? sys_path_hooks : Py_None) < 0) {
-        Py_XDECREF(sys_path_hooks);
-        Py_XDECREF(sys_meta_path);
-        Py_XDECREF(sys_path);
-        return NULL;
-    }
-    Py_XDECREF(sys_path_hooks);
-    Py_XDECREF(sys_meta_path);
-    Py_XDECREF(sys_path);
-
-
     /* XOptions is initialized after first some imports.
      * So we can't have negative cache before completed initialization.
      * Anyway, importlib._find_and_load is much slower than

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to