Skip to content

Commit 2fd43a1

Browse files
authored
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
1 parent 7257b24 commit 2fd43a1

File tree

8 files changed

+98
-27
lines changed

8 files changed

+98
-27
lines changed

Lib/importlib/_bootstrap.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,14 @@ def _sanity_check(name, package, level):
13071307

13081308
def _find_and_load_unlocked(name, import_):
13091309
path = None
1310+
sys.audit(
1311+
"import",
1312+
name,
1313+
path,
1314+
getattr(sys, "path", None),
1315+
getattr(sys, "meta_path", None),
1316+
getattr(sys, "path_hooks", None)
1317+
)
13101318
parent = name.rpartition('.')[0]
13111319
parent_spec = None
13121320
if parent:

Lib/test/audit-tests.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import contextlib
99
import os
1010
import sys
11+
import unittest.mock
12+
from test.support import swap_item
1113

1214

1315
class TestHook:
@@ -672,6 +674,84 @@ def hook(event, args):
672674
assertEqual(event_script_path, tmp_file.name)
673675
assertEqual(remote_event_script_path, tmp_file.name)
674676

677+
def test_import_module():
678+
import importlib
679+
680+
with TestHook() as hook:
681+
importlib.import_module("importlib") # already imported, won't get logged
682+
importlib.import_module("email") # standard library module
683+
importlib.import_module("pythoninfo") # random module
684+
importlib.import_module(".audit_test_data.submodule", "test") # relative import
685+
importlib.import_module("test.audit_test_data.submodule2") # absolute import
686+
importlib.import_module("_testcapi") # extension module
687+
688+
actual = [a for e, a in hook.seen if e == "import"]
689+
assertSequenceEqual(
690+
[
691+
("email", None, sys.path, sys.meta_path, sys.path_hooks),
692+
("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
693+
("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks),
694+
("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks),
695+
("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks),
696+
("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
697+
("_testcapi", unittest.mock.ANY, None, None, None)
698+
],
699+
actual,
700+
)
701+
702+
def test_builtin__import__():
703+
import importlib # noqa: F401
704+
705+
with TestHook() as hook:
706+
__import__("importlib")
707+
__import__("email")
708+
__import__("pythoninfo")
709+
__import__("audit_test_data.submodule", level=1, globals={"__package__": "test"})
710+
__import__("test.audit_test_data.submodule2")
711+
__import__("_testcapi")
712+
713+
actual = [a for e, a in hook.seen if e == "import"]
714+
assertSequenceEqual(
715+
[
716+
("email", None, sys.path, sys.meta_path, sys.path_hooks),
717+
("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
718+
("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks),
719+
("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks),
720+
("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks),
721+
("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
722+
("_testcapi", unittest.mock.ANY, None, None, None)
723+
],
724+
actual,
725+
)
726+
727+
def test_import_statement():
728+
import importlib # noqa: F401
729+
# Set __package__ so relative imports work
730+
with swap_item(globals(), "__package__", "test"):
731+
with TestHook() as hook:
732+
import importlib # noqa: F401
733+
import email # noqa: F401
734+
import pythoninfo # noqa: F401
735+
from .audit_test_data import submodule # noqa: F401
736+
import test.audit_test_data.submodule2 # noqa: F401
737+
import _testcapi # noqa: F401
738+
739+
actual = [a for e, a in hook.seen if e == "import"]
740+
# Import statement ordering is different because the package is
741+
# loaded first and then the submodule
742+
assertSequenceEqual(
743+
[
744+
("email", None, sys.path, sys.meta_path, sys.path_hooks),
745+
("pythoninfo", None, sys.path, sys.meta_path, sys.path_hooks),
746+
("test.audit_test_data", None, sys.path, sys.meta_path, sys.path_hooks),
747+
("test.audit_test_data.submodule", None, sys.path, sys.meta_path, sys.path_hooks),
748+
("test.audit_test_data.submodule2", None, sys.path, sys.meta_path, sys.path_hooks),
749+
("_testcapi", None, sys.path, sys.meta_path, sys.path_hooks),
750+
("_testcapi", unittest.mock.ANY, None, None, None)
751+
],
752+
actual,
753+
)
754+
675755
if __name__ == "__main__":
676756
from test.support import suppress_msvcrt_asserts
677757

Lib/test/audit_test_data/__init__.py

Whitespace-only changes.

Lib/test/audit_test_data/submodule.py

Whitespace-only changes.

Lib/test/audit_test_data/submodule2.py

Whitespace-only changes.

Lib/test/test_audit.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,5 +331,14 @@ def test_sys_remote_exec(self):
331331
if returncode:
332332
self.fail(stderr)
333333

334+
def test_import_module(self):
335+
self.do_test("test_import_module")
336+
337+
def test_builtin__import__(self):
338+
self.do_test("test_builtin__import__")
339+
340+
def test_import_statement(self):
341+
self.do_test("test_import_statement")
342+
334343
if __name__ == "__main__":
335344
unittest.main()

Makefile.pre.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2594,6 +2594,7 @@ TESTSUBDIRS= idlelib/idle_test \
25942594
test/test_ast \
25952595
test/test_ast/data \
25962596
test/archivetestdata \
2597+
test/audit_test_data \
25972598
test/audiodata \
25982599
test/certdata \
25992600
test/certdata/capath \

Python/import.c

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3681,33 +3681,6 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
36813681

36823682
PyTime_t t1 = 0, accumulated_copy = accumulated;
36833683

3684-
PyObject *sys_path, *sys_meta_path, *sys_path_hooks;
3685-
if (PySys_GetOptionalAttrString("path", &sys_path) < 0) {
3686-
return NULL;
3687-
}
3688-
if (PySys_GetOptionalAttrString("meta_path", &sys_meta_path) < 0) {
3689-
Py_XDECREF(sys_path);
3690-
return NULL;
3691-
}
3692-
if (PySys_GetOptionalAttrString("path_hooks", &sys_path_hooks) < 0) {
3693-
Py_XDECREF(sys_meta_path);
3694-
Py_XDECREF(sys_path);
3695-
return NULL;
3696-
}
3697-
if (_PySys_Audit(tstate, "import", "OOOOO",
3698-
abs_name, Py_None, sys_path ? sys_path : Py_None,
3699-
sys_meta_path ? sys_meta_path : Py_None,
3700-
sys_path_hooks ? sys_path_hooks : Py_None) < 0) {
3701-
Py_XDECREF(sys_path_hooks);
3702-
Py_XDECREF(sys_meta_path);
3703-
Py_XDECREF(sys_path);
3704-
return NULL;
3705-
}
3706-
Py_XDECREF(sys_path_hooks);
3707-
Py_XDECREF(sys_meta_path);
3708-
Py_XDECREF(sys_path);
3709-
3710-
37113684
/* XOptions is initialized after first some imports.
37123685
* So we can't have negative cache before completed initialization.
37133686
* Anyway, importlib._find_and_load is much slower than

0 commit comments

Comments
 (0)