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_):
|
def _find_and_load_unlocked(name, import_):
|
||||||
path = None
|
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 = name.rpartition('.')[0]
|
||||||
parent_spec = None
|
parent_spec = None
|
||||||
if parent:
|
if parent:
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ module with arguments identifying each test.
|
|||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import unittest.mock
|
||||||
|
from test.support import swap_item
|
||||||
|
|
||||||
|
|
||||||
class TestHook:
|
class TestHook:
|
||||||
@@ -672,6 +674,84 @@ def test_sys_remote_exec():
|
|||||||
assertEqual(event_script_path, tmp_file.name)
|
assertEqual(event_script_path, tmp_file.name)
|
||||||
assertEqual(remote_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__":
|
if __name__ == "__main__":
|
||||||
from test.support import suppress_msvcrt_asserts
|
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:
|
if returncode:
|
||||||
self.fail(stderr)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -2594,6 +2594,7 @@ TESTSUBDIRS= idlelib/idle_test \
|
|||||||
test/test_ast \
|
test/test_ast \
|
||||||
test/test_ast/data \
|
test/test_ast/data \
|
||||||
test/archivetestdata \
|
test/archivetestdata \
|
||||||
|
test/audit_test_data \
|
||||||
test/audiodata \
|
test/audiodata \
|
||||||
test/certdata \
|
test/certdata \
|
||||||
test/certdata/capath \
|
test/certdata/capath \
|
||||||
|
|||||||
@@ -3681,33 +3681,6 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
|
|||||||
|
|
||||||
PyTime_t t1 = 0, accumulated_copy = accumulated;
|
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.
|
/* XOptions is initialized after first some imports.
|
||||||
* So we can't have negative cache before completed initialization.
|
* So we can't have negative cache before completed initialization.
|
||||||
* Anyway, importlib._find_and_load is much slower than
|
* Anyway, importlib._find_and_load is much slower than
|
||||||
|
|||||||
Reference in New Issue
Block a user