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
This commit is contained in:
@@ -1307,6 +1307,14 @@ _ERR_MSG_PREFIX = 'No module named '
|
||||
|
||||
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:
|
||||
|
||||
@@ -8,6 +8,8 @@ module with arguments identifying each test.
|
||||
import contextlib
|
||||
import os
|
||||
import sys
|
||||
import unittest.mock
|
||||
from test.support import swap_item
|
||||
|
||||
|
||||
class TestHook:
|
||||
@@ -672,6 +674,84 @@ def test_sys_remote_exec():
|
||||
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
|
||||
|
||||
|
||||
0
Lib/test/audit_test_data/__init__.py
Normal file
0
Lib/test/audit_test_data/__init__.py
Normal file
0
Lib/test/audit_test_data/submodule.py
Normal file
0
Lib/test/audit_test_data/submodule.py
Normal file
0
Lib/test/audit_test_data/submodule2.py
Normal file
0
Lib/test/audit_test_data/submodule2.py
Normal file
@@ -331,5 +331,14 @@ class AuditTest(unittest.TestCase):
|
||||
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()
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user