Skip to content

Commit 1d8342b

Browse files
committed
gh-128911: Add tests on the PyImport C API
* Add Modules/_testlimitedcapi/import.c * Add Lib/test/test_capi/test_import.py
1 parent 313b96e commit 1d8342b

File tree

5 files changed

+264
-1
lines changed

5 files changed

+264
-1
lines changed

Lib/test/test_capi/test_import.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import importlib.util
2+
import sys
3+
import types
4+
import unittest
5+
from test.support import import_helper
6+
from test.support.warnings_helper import check_warnings
7+
8+
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
9+
10+
11+
class ImportTests(unittest.TestCase):
12+
def test_getmagicnumber(self):
13+
# Test PyImport_GetMagicNumber()
14+
magic = _testlimitedcapi.PyImport_GetMagicNumber()
15+
self.assertEqual(magic,
16+
int.from_bytes(importlib.util.MAGIC_NUMBER, 'little'))
17+
18+
def test_getmagictag(self):
19+
# Test PyImport_GetMagicTag()
20+
tag = _testlimitedcapi.PyImport_GetMagicTag()
21+
self.assertEqual(tag, sys.implementation.cache_tag)
22+
23+
def test_getmoduledict(self):
24+
# Test PyImport_GetModuleDict()
25+
modules = _testlimitedcapi.PyImport_GetModuleDict()
26+
self.assertIs(modules, sys.modules)
27+
28+
def check_import_loaded_module(self, import_module):
29+
for name in ('os', 'sys', 'test', 'unittest'):
30+
with self.subTest(name=name):
31+
self.assertIn(name, sys.modules)
32+
module = import_module(name)
33+
self.assertIsInstance(module, types.ModuleType)
34+
self.assertIs(module, sys.modules[name])
35+
36+
def check_import_fresh_module(self, import_module):
37+
old_modules = dict(sys.modules)
38+
try:
39+
for name in ('asyncio', 'colorsys', 'datetime'):
40+
with self.subTest(name=name):
41+
sys.modules.pop(name, None)
42+
module = import_module(name)
43+
self.assertIsInstance(module, types.ModuleType)
44+
self.assertIs(module, sys.modules[name])
45+
self.assertEqual(module.__name__, name)
46+
finally:
47+
sys.modules.clear()
48+
sys.modules.update(old_modules)
49+
50+
def test_getmodule(self):
51+
# Test PyImport_GetModule()
52+
self.check_import_loaded_module(_testlimitedcapi.PyImport_GetModule)
53+
54+
nonexistent = 'nonexistent'
55+
self.assertNotIn(nonexistent, sys.modules)
56+
self.assertIsNone(_testlimitedcapi.PyImport_GetModule(nonexistent))
57+
self.assertIsNone(_testlimitedcapi.PyImport_GetModule(''))
58+
self.assertIsNone(_testlimitedcapi.PyImport_GetModule(object()))
59+
60+
def check_addmodule(self, add_module):
61+
# create a new module
62+
name = 'nonexistent'
63+
self.assertNotIn(name, sys.modules)
64+
try:
65+
module = add_module(name)
66+
self.assertIsInstance(module, types.ModuleType)
67+
self.assertIs(module, sys.modules[name])
68+
finally:
69+
sys.modules.pop(name, None)
70+
71+
# get an existing module
72+
self.check_import_loaded_module(add_module)
73+
74+
def test_addmoduleobject(self):
75+
# Test PyImport_AddModuleObject()
76+
self.check_addmodule(_testlimitedcapi.PyImport_AddModuleObject)
77+
78+
def test_addmodule(self):
79+
# Test PyImport_AddModule()
80+
self.check_addmodule(_testlimitedcapi.PyImport_AddModule)
81+
82+
def test_addmoduleref(self):
83+
# Test PyImport_AddModuleRef()
84+
self.check_addmodule(_testlimitedcapi.PyImport_AddModuleRef)
85+
86+
def check_import_func(self, import_module):
87+
self.check_import_loaded_module(import_module)
88+
self.check_import_fresh_module(import_module)
89+
90+
# Invalid module name types
91+
with self.assertRaises(TypeError):
92+
import_module(123)
93+
with self.assertRaises(TypeError):
94+
import_module(object())
95+
96+
def test_import(self):
97+
# Test PyImport_Import()
98+
self.check_import_func(_testlimitedcapi.PyImport_Import)
99+
100+
def test_importmodule(self):
101+
# Test PyImport_ImportModule()
102+
self.check_import_func(_testlimitedcapi.PyImport_ImportModule)
103+
104+
def test_importmodulenoblock(self):
105+
# Test deprecated PyImport_ImportModuleNoBlock()
106+
with check_warnings(('', DeprecationWarning)):
107+
self.check_import_func(_testlimitedcapi.PyImport_ImportModuleNoBlock)
108+
109+
# TODO: test PyImport_ExecCodeModule()
110+
# TODO: test PyImport_ExecCodeModuleEx()
111+
# TODO: test PyImport_ExecCodeModuleWithPathnames()
112+
# TODO: test PyImport_ExecCodeModuleObject()
113+
# TODO: test PyImport_ImportModuleLevel()
114+
# TODO: test PyImport_ImportModuleLevelObject()
115+
# TODO: test PyImport_ImportModuleEx()
116+
# TODO: test PyImport_GetImporter()
117+
# TODO: test PyImport_ReloadModule()
118+
# TODO: test PyImport_ImportFrozenModuleObject()
119+
# TODO: test PyImport_ImportFrozenModule()
120+
# TODO: test PyImport_AppendInittab()
121+
# TODO: test PyImport_ExtendInittab()
122+
123+
124+
if __name__ == "__main__":
125+
unittest.main()

Modules/Setup.stdlib.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
164164
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
165165
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c
166-
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c
166+
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c
167167
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
168168
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
169169

Modules/_testlimitedcapi.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ PyInit__testlimitedcapi(void)
5656
if (_PyTestLimitedCAPI_Init_HeaptypeRelative(mod) < 0) {
5757
return NULL;
5858
}
59+
if (_PyTestLimitedCAPI_Init_Import(mod) < 0) {
60+
return NULL;
61+
}
5962
if (_PyTestLimitedCAPI_Init_List(mod) < 0) {
6063
return NULL;
6164
}

Modules/_testlimitedcapi/import.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#include "parts.h"
2+
#include "util.h"
3+
4+
5+
/* Test PyImport_GetMagicNumber() */
6+
static PyObject *
7+
pyimport_getmagicnumber(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
8+
{
9+
long magic = PyImport_GetMagicNumber();
10+
return PyLong_FromLong(magic);
11+
}
12+
13+
14+
/* Test PyImport_GetMagicTag() */
15+
static PyObject *
16+
pyimport_getmagictag(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
17+
{
18+
const char *tag = PyImport_GetMagicTag();
19+
return PyUnicode_FromString(tag);
20+
}
21+
22+
23+
/* Test PyImport_GetModuleDict() */
24+
static PyObject *
25+
pyimport_getmoduledict(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
26+
{
27+
return Py_XNewRef(PyImport_GetModuleDict());
28+
}
29+
30+
31+
/* Test PyImport_GetModule() */
32+
static PyObject *
33+
pyimport_getmodule(PyObject *Py_UNUSED(module), PyObject *name)
34+
{
35+
assert(!PyErr_Occurred());
36+
PyObject *module = PyImport_GetModule(name);
37+
if (module == NULL && !PyErr_Occurred()) {
38+
Py_RETURN_NONE;
39+
}
40+
return module;
41+
}
42+
43+
44+
/* Test PyImport_AddModuleObject() */
45+
static PyObject *
46+
pyimport_addmoduleobject(PyObject *Py_UNUSED(module), PyObject *name)
47+
{
48+
return Py_XNewRef(PyImport_AddModuleObject(name));
49+
}
50+
51+
52+
/* Test PyImport_AddModule() */
53+
static PyObject *
54+
pyimport_addmodule(PyObject *Py_UNUSED(module), PyObject *args)
55+
{
56+
const char *name;
57+
if (!PyArg_ParseTuple(args, "s", &name)) {
58+
return NULL;
59+
}
60+
61+
return Py_XNewRef(PyImport_AddModule(name));
62+
}
63+
64+
65+
/* Test PyImport_AddModuleRef() */
66+
static PyObject *
67+
pyimport_addmoduleref(PyObject *Py_UNUSED(module), PyObject *args)
68+
{
69+
const char *name;
70+
if (!PyArg_ParseTuple(args, "s", &name)) {
71+
return NULL;
72+
}
73+
74+
return PyImport_AddModuleRef(name);
75+
}
76+
77+
78+
/* Test PyImport_Import() */
79+
static PyObject *
80+
pyimport_import(PyObject *Py_UNUSED(module), PyObject *name)
81+
{
82+
return PyImport_Import(name);
83+
}
84+
85+
86+
/* Test PyImport_ImportModule() */
87+
static PyObject *
88+
pyimport_importmodule(PyObject *Py_UNUSED(module), PyObject *args)
89+
{
90+
const char *name;
91+
if (!PyArg_ParseTuple(args, "s", &name)) {
92+
return NULL;
93+
}
94+
95+
return PyImport_ImportModule(name);
96+
}
97+
98+
99+
/* Test PyImport_ImportModuleNoBlock() */
100+
static PyObject *
101+
pyimport_importmodulenoblock(PyObject *Py_UNUSED(module), PyObject *args)
102+
{
103+
const char *name;
104+
if (!PyArg_ParseTuple(args, "s", &name)) {
105+
return NULL;
106+
}
107+
108+
_Py_COMP_DIAG_PUSH
109+
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
110+
return PyImport_ImportModuleNoBlock(name);
111+
_Py_COMP_DIAG_POP
112+
}
113+
114+
115+
static PyMethodDef test_methods[] = {
116+
{"PyImport_GetMagicNumber", pyimport_getmagicnumber, METH_NOARGS},
117+
{"PyImport_GetMagicTag", pyimport_getmagictag, METH_NOARGS},
118+
{"PyImport_GetModuleDict", pyimport_getmoduledict, METH_NOARGS},
119+
{"PyImport_GetModule", pyimport_getmodule, METH_O},
120+
{"PyImport_AddModuleObject", pyimport_addmoduleobject, METH_O},
121+
{"PyImport_AddModule", pyimport_addmodule, METH_VARARGS},
122+
{"PyImport_AddModuleRef", pyimport_addmoduleref, METH_VARARGS},
123+
{"PyImport_Import", pyimport_import, METH_O},
124+
{"PyImport_ImportModule", pyimport_importmodule, METH_VARARGS},
125+
{"PyImport_ImportModuleNoBlock", pyimport_importmodulenoblock, METH_VARARGS},
126+
{NULL},
127+
};
128+
129+
130+
int
131+
_PyTestLimitedCAPI_Init_Import(PyObject *module)
132+
{
133+
return PyModule_AddFunctions(module, test_methods);
134+
}

Modules/_testlimitedcapi/parts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ int _PyTestLimitedCAPI_Init_Dict(PyObject *module);
3131
int _PyTestLimitedCAPI_Init_Eval(PyObject *module);
3232
int _PyTestLimitedCAPI_Init_Float(PyObject *module);
3333
int _PyTestLimitedCAPI_Init_HeaptypeRelative(PyObject *module);
34+
int _PyTestLimitedCAPI_Init_Import(PyObject *module);
3435
int _PyTestLimitedCAPI_Init_Object(PyObject *module);
3536
int _PyTestLimitedCAPI_Init_List(PyObject *module);
3637
int _PyTestLimitedCAPI_Init_Long(PyObject *module);

0 commit comments

Comments
 (0)