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]