Skip to content

Commit 33b8507

Browse files
committed
Add tests
Avoid the datetime module.
1 parent fc2f8cd commit 33b8507

File tree

2 files changed

+282
-22
lines changed

2 files changed

+282
-22
lines changed

Lib/test/test_capi/test_import.py

+136-21
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import importlib.util
2+
import os.path
23
import sys
34
import types
45
import unittest
56
from test.support import import_helper
67
from test.support.warnings_helper import check_warnings
78

89
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
10+
NULL = None
911

1012

1113
class ImportTests(unittest.TestCase):
@@ -36,7 +38,7 @@ def check_import_loaded_module(self, import_module):
3638
def check_import_fresh_module(self, import_module):
3739
old_modules = dict(sys.modules)
3840
try:
39-
for name in ('colorsys', 'datetime', 'math'):
41+
for name in ('colorsys', 'math'):
4042
with self.subTest(name=name):
4143
sys.modules.pop(name, None)
4244
module = import_module(name)
@@ -57,32 +59,43 @@ def test_getmodule(self):
5759
self.assertIsNone(_testlimitedcapi.PyImport_GetModule(''))
5860
self.assertIsNone(_testlimitedcapi.PyImport_GetModule(object()))
5961

60-
def check_addmodule(self, add_module):
62+
def check_addmodule(self, add_module, accept_nonstr=False):
6163
# 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)
64+
names = ['nonexistent']
65+
if accept_nonstr:
66+
# PyImport_AddModuleObject() accepts non-string names
67+
names.append(object())
68+
for name in names:
69+
with self.subTest(name=name):
70+
self.assertNotIn(name, sys.modules)
71+
try:
72+
module = add_module(name)
73+
self.assertIsInstance(module, types.ModuleType)
74+
self.assertEqual(module.__name__, name)
75+
self.assertIs(module, sys.modules[name])
76+
finally:
77+
sys.modules.pop(name, None)
7078

7179
# get an existing module
7280
self.check_import_loaded_module(add_module)
7381

7482
def test_addmoduleobject(self):
7583
# Test PyImport_AddModuleObject()
76-
self.check_addmodule(_testlimitedcapi.PyImport_AddModuleObject)
84+
self.check_addmodule(_testlimitedcapi.PyImport_AddModuleObject,
85+
accept_nonstr=True)
7786

7887
def test_addmodule(self):
7988
# Test PyImport_AddModule()
8089
self.check_addmodule(_testlimitedcapi.PyImport_AddModule)
8190

91+
# CRASHES PyImport_AddModule(NULL)
92+
8293
def test_addmoduleref(self):
8394
# Test PyImport_AddModuleRef()
8495
self.check_addmodule(_testlimitedcapi.PyImport_AddModuleRef)
8596

97+
# CRASHES PyImport_AddModuleRef(NULL)
98+
8699
def check_import_func(self, import_module):
87100
self.check_import_loaded_module(import_module)
88101
self.check_import_fresh_module(import_module)
@@ -106,19 +119,121 @@ def test_importmodulenoblock(self):
106119
with check_warnings(('', DeprecationWarning)):
107120
self.check_import_func(_testlimitedcapi.PyImport_ImportModuleNoBlock)
108121

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()
122+
def check_frozen_import(self, import_frozen_module):
123+
# Importing a frozen module executes its code, so starts by unloading
124+
# the module to execute the code in a new (temporary) module.
125+
old_zipimport = sys.modules.pop('zipimport')
126+
try:
127+
self.assertEqual(import_frozen_module('zipimport'), 1)
128+
finally:
129+
sys.modules['zipimport'] = old_zipimport
130+
131+
# not a frozen module
132+
self.assertEqual(import_frozen_module('sys'), 0)
133+
134+
def test_importfrozenmodule(self):
135+
# Test PyImport_ImportFrozenModule()
136+
self.check_frozen_import(_testlimitedcapi.PyImport_ImportFrozenModule)
137+
138+
# CRASHES PyImport_ImportFrozenModule(NULL)
139+
140+
def test_importfrozenmoduleobject(self):
141+
# Test PyImport_ImportFrozenModuleObject()
142+
PyImport_ImportFrozenModuleObject = _testlimitedcapi.PyImport_ImportFrozenModuleObject
143+
self.check_frozen_import(PyImport_ImportFrozenModuleObject)
144+
145+
# Bad name is treated as "not found"
146+
self.assertEqual(PyImport_ImportFrozenModuleObject(None), 0)
147+
148+
def test_importmoduleex(self):
149+
# Test PyImport_ImportModuleEx()
150+
def import_module(name):
151+
return _testlimitedcapi.PyImport_ImportModuleEx(
152+
name, globals(), {}, [])
153+
154+
self.check_import_func(import_module)
155+
156+
def test_importmodulelevel(self):
157+
# Test PyImport_ImportModuleLevel()
158+
def import_module(name):
159+
return _testlimitedcapi.PyImport_ImportModuleLevel(
160+
name, globals(), {}, [], 0)
161+
162+
self.check_import_func(import_module)
163+
164+
def test_importmodulelevelobject(self):
165+
# Test PyImport_ImportModuleLevelObject()
166+
def import_module(name):
167+
return _testlimitedcapi.PyImport_ImportModuleLevelObject(
168+
name, globals(), {}, [], 0)
169+
170+
self.check_import_func(import_module)
171+
172+
def check_executecodemodule(self, execute_code, pathname=None):
173+
name = 'test_import_executecode'
174+
try:
175+
# Create a temporary module where the code will be executed
176+
self.assertNotIn(name, sys.modules)
177+
module = _testlimitedcapi.PyImport_AddModuleRef(name)
178+
self.assertNotHasAttr(module, 'attr')
179+
180+
# Execute the code
181+
if pathname is not None:
182+
code_filename = pathname
183+
else:
184+
code_filename = '<string>'
185+
code = compile('attr = 1', code_filename, 'exec')
186+
module2 = execute_code(name, code)
187+
self.assertIs(module2, module)
188+
189+
# Check the function side effects
190+
self.assertEqual(module.attr, 1)
191+
if pathname is not None:
192+
self.assertEqual(module.__spec__.origin, pathname)
193+
finally:
194+
sys.modules.pop(name, None)
195+
196+
def test_executecodemodule(self):
197+
# Test PyImport_ExecCodeModule()
198+
self.check_executecodemodule(_testlimitedcapi.PyImport_ExecCodeModule)
199+
200+
def test_executecodemoduleex(self):
201+
# Test PyImport_ExecCodeModuleEx()
202+
pathname = os.path.abspath('pathname')
203+
204+
def execute_code(name, code):
205+
return _testlimitedcapi.PyImport_ExecCodeModuleEx(name, code,
206+
pathname)
207+
self.check_executecodemodule(execute_code, pathname)
208+
209+
def check_executecode_pathnames(self, execute_code_func):
210+
# Test non-NULL pathname and NULL cpathname
211+
pathname = os.path.abspath('pathname')
212+
213+
def execute_code1(name, code):
214+
return execute_code_func(name, code, pathname, NULL)
215+
self.check_executecodemodule(execute_code1, pathname)
216+
217+
# Test NULL pathname and non-NULL cpathname
218+
pyc_filename = importlib.util.cache_from_source(__file__)
219+
py_filename = importlib.util.source_from_cache(pyc_filename)
220+
221+
def execute_code2(name, code):
222+
return execute_code_func(name, code, NULL, pyc_filename)
223+
self.check_executecodemodule(execute_code2, py_filename)
224+
225+
def test_executecodemodulewithpathnames(self):
226+
# Test PyImport_ExecCodeModuleWithPathnames()
227+
self.check_executecode_pathnames(_testlimitedcapi.PyImport_ExecCodeModuleWithPathnames)
228+
229+
def test_executecodemoduleobject(self):
230+
# Test PyImport_ExecCodeModuleObject()
231+
self.check_executecode_pathnames(_testlimitedcapi.PyImport_ExecCodeModuleObject)
232+
116233
# TODO: test PyImport_GetImporter()
117234
# TODO: test PyImport_ReloadModule()
118-
# TODO: test PyImport_ImportFrozenModuleObject()
119-
# TODO: test PyImport_ImportFrozenModule()
120-
# TODO: test PyImport_AppendInittab()
121235
# TODO: test PyImport_ExtendInittab()
236+
# PyImport_AppendInittab() is tested by test_embed
122237

123238

124239
if __name__ == "__main__":

Modules/_testlimitedcapi/import.c

+146-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Need limited C API version 3.7 for PyImport_GetModule()
1+
// Need limited C API version 3.13 for PyImport_AddModuleRef()
22
#include "pyconfig.h" // Py_GIL_DISABLED
33
#if !defined(Py_GIL_DISABLED) && !defined(Py_LIMITED_API)
44
# define Py_LIMITED_API 0x030d0000
@@ -118,6 +118,142 @@ pyimport_importmodulenoblock(PyObject *Py_UNUSED(module), PyObject *args)
118118
}
119119

120120

121+
/* Test PyImport_ImportModuleEx() */
122+
static PyObject *
123+
pyimport_importmoduleex(PyObject *Py_UNUSED(module), PyObject *args)
124+
{
125+
const char *name;
126+
PyObject *globals, *locals, *fromlist;
127+
if (!PyArg_ParseTuple(args, "sOOO",
128+
&name, &globals, &locals, &fromlist)) {
129+
return NULL;
130+
}
131+
132+
return PyImport_ImportModuleEx(name, globals, locals, fromlist);
133+
}
134+
135+
136+
/* Test PyImport_ImportModuleLevel() */
137+
static PyObject *
138+
pyimport_importmodulelevel(PyObject *Py_UNUSED(module), PyObject *args)
139+
{
140+
const char *name;
141+
PyObject *globals, *locals, *fromlist;
142+
int level;
143+
if (!PyArg_ParseTuple(args, "sOOOi",
144+
&name, &globals, &locals, &fromlist, &level)) {
145+
return NULL;
146+
}
147+
148+
return PyImport_ImportModuleLevel(name, globals, locals, fromlist, level);
149+
}
150+
151+
152+
/* Test PyImport_ImportModuleLevelObject() */
153+
static PyObject *
154+
pyimport_importmodulelevelobject(PyObject *Py_UNUSED(module), PyObject *args)
155+
{
156+
PyObject *name, *globals, *locals, *fromlist;
157+
int level;
158+
if (!PyArg_ParseTuple(args, "OOOOi",
159+
&name, &globals, &locals, &fromlist, &level)) {
160+
return NULL;
161+
}
162+
163+
return PyImport_ImportModuleLevelObject(name, globals, locals, fromlist, level);
164+
}
165+
166+
167+
/* Test PyImport_ImportFrozenModule() */
168+
static PyObject *
169+
pyimport_importfrozenmodule(PyObject *Py_UNUSED(module), PyObject *args)
170+
{
171+
const char *name;
172+
if (!PyArg_ParseTuple(args, "s", &name)) {
173+
return NULL;
174+
}
175+
176+
int res = PyImport_ImportFrozenModule(name);
177+
if (res < 0) {
178+
return NULL;
179+
}
180+
return PyLong_FromLong(res);
181+
}
182+
183+
184+
/* Test PyImport_ImportFrozenModuleObject() */
185+
static PyObject *
186+
pyimport_importfrozenmoduleobject(PyObject *Py_UNUSED(module), PyObject *name)
187+
{
188+
int res = PyImport_ImportFrozenModuleObject(name);
189+
if (res < 0) {
190+
return NULL;
191+
}
192+
return PyLong_FromLong(res);
193+
}
194+
195+
196+
/* Test PyImport_ExecCodeModule() */
197+
static PyObject *
198+
pyimport_executecodemodule(PyObject *Py_UNUSED(module), PyObject *args)
199+
{
200+
const char *name;
201+
PyObject *code;
202+
if (!PyArg_ParseTuple(args, "sO", &name, &code)) {
203+
return NULL;
204+
}
205+
206+
return PyImport_ExecCodeModule(name, code);
207+
}
208+
209+
210+
/* Test PyImport_ExecCodeModuleEx() */
211+
static PyObject *
212+
pyimport_executecodemoduleex(PyObject *Py_UNUSED(module), PyObject *args)
213+
{
214+
const char *name;
215+
PyObject *code;
216+
const char *pathname;
217+
if (!PyArg_ParseTuple(args, "sOs", &name, &code, &pathname)) {
218+
return NULL;
219+
}
220+
221+
return PyImport_ExecCodeModuleEx(name, code, pathname);
222+
}
223+
224+
225+
/* Test PyImport_ExecCodeModuleWithPathnames() */
226+
static PyObject *
227+
pyimport_executecodemodulewithpathnames(PyObject *Py_UNUSED(module), PyObject *args)
228+
{
229+
const char *name;
230+
PyObject *code;
231+
const char *pathname;
232+
const char *cpathname;
233+
if (!PyArg_ParseTuple(args, "sOzz", &name, &code, &pathname, &cpathname)) {
234+
return NULL;
235+
}
236+
237+
return PyImport_ExecCodeModuleWithPathnames(name, code,
238+
pathname, cpathname);
239+
}
240+
241+
242+
/* Test PyImport_ExecCodeModuleObject() */
243+
static PyObject *
244+
pyimport_executecodemoduleobject(PyObject *Py_UNUSED(module), PyObject *args)
245+
{
246+
PyObject *name, *code, *pathname, *cpathname;
247+
if (!PyArg_ParseTuple(args, "OOOO", &name, &code, &pathname, &cpathname)) {
248+
return NULL;
249+
}
250+
NULLABLE(pathname);
251+
NULLABLE(cpathname);
252+
253+
return PyImport_ExecCodeModuleObject(name, code, pathname, cpathname);
254+
}
255+
256+
121257
static PyMethodDef test_methods[] = {
122258
{"PyImport_GetMagicNumber", pyimport_getmagicnumber, METH_NOARGS},
123259
{"PyImport_GetMagicTag", pyimport_getmagictag, METH_NOARGS},
@@ -129,6 +265,15 @@ static PyMethodDef test_methods[] = {
129265
{"PyImport_Import", pyimport_import, METH_O},
130266
{"PyImport_ImportModule", pyimport_importmodule, METH_VARARGS},
131267
{"PyImport_ImportModuleNoBlock", pyimport_importmodulenoblock, METH_VARARGS},
268+
{"PyImport_ImportModuleEx", pyimport_importmoduleex, METH_VARARGS},
269+
{"PyImport_ImportModuleLevel", pyimport_importmodulelevel, METH_VARARGS},
270+
{"PyImport_ImportModuleLevelObject", pyimport_importmodulelevelobject, METH_VARARGS},
271+
{"PyImport_ImportFrozenModule", pyimport_importfrozenmodule, METH_VARARGS},
272+
{"PyImport_ImportFrozenModuleObject", pyimport_importfrozenmoduleobject, METH_O},
273+
{"PyImport_ExecCodeModule", pyimport_executecodemodule, METH_VARARGS},
274+
{"PyImport_ExecCodeModuleEx", pyimport_executecodemoduleex, METH_VARARGS},
275+
{"PyImport_ExecCodeModuleWithPathnames", pyimport_executecodemodulewithpathnames, METH_VARARGS},
276+
{"PyImport_ExecCodeModuleObject", pyimport_executecodemoduleobject, METH_VARARGS},
132277
{NULL},
133278
};
134279

0 commit comments

Comments
 (0)