python (3.12.0)
1 from warnings import catch_warnings
2 from test.test_importlib import abc, util
3
4 machinery = util.import_importlib('importlib.machinery')
5
6 import os.path
7 import sys
8 import types
9 import unittest
10 import warnings
11 import importlib.util
12 import importlib
13 from test.support.script_helper import assert_python_failure
14
15
16 class ESC[4;38;5;81mLoaderTests:
17
18 """Test ExtensionFileLoader."""
19
20 def setUp(self):
21 if not self.machinery.EXTENSION_SUFFIXES:
22 raise unittest.SkipTest("Requires dynamic loading support.")
23 if util.EXTENSIONS.name in sys.builtin_module_names:
24 raise unittest.SkipTest(
25 f"{util.EXTENSIONS.name} is a builtin module"
26 )
27 self.loader = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name,
28 util.EXTENSIONS.file_path)
29
30 def load_module(self, fullname):
31 with warnings.catch_warnings():
32 warnings.simplefilter("ignore", DeprecationWarning)
33 return self.loader.load_module(fullname)
34
35 def test_equality(self):
36 other = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name,
37 util.EXTENSIONS.file_path)
38 self.assertEqual(self.loader, other)
39
40 def test_inequality(self):
41 other = self.machinery.ExtensionFileLoader('_' + util.EXTENSIONS.name,
42 util.EXTENSIONS.file_path)
43 self.assertNotEqual(self.loader, other)
44
45 def test_load_module_API(self):
46 # Test the default argument for load_module().
47 with warnings.catch_warnings():
48 warnings.simplefilter("ignore", DeprecationWarning)
49 self.loader.load_module()
50 self.loader.load_module(None)
51 with self.assertRaises(ImportError):
52 self.load_module('XXX')
53
54 def test_module(self):
55 with util.uncache(util.EXTENSIONS.name):
56 module = self.load_module(util.EXTENSIONS.name)
57 for attr, value in [('__name__', util.EXTENSIONS.name),
58 ('__file__', util.EXTENSIONS.file_path),
59 ('__package__', '')]:
60 self.assertEqual(getattr(module, attr), value)
61 self.assertIn(util.EXTENSIONS.name, sys.modules)
62 self.assertIsInstance(module.__loader__,
63 self.machinery.ExtensionFileLoader)
64
65 # No extension module as __init__ available for testing.
66 test_package = None
67
68 # No extension module in a package available for testing.
69 test_lacking_parent = None
70
71 # No easy way to trigger a failure after a successful import.
72 test_state_after_failure = None
73
74 def test_unloadable(self):
75 name = 'asdfjkl;'
76 with self.assertRaises(ImportError) as cm:
77 self.load_module(name)
78 self.assertEqual(cm.exception.name, name)
79
80 def test_module_reuse(self):
81 with util.uncache(util.EXTENSIONS.name):
82 module1 = self.load_module(util.EXTENSIONS.name)
83 module2 = self.load_module(util.EXTENSIONS.name)
84 self.assertIs(module1, module2)
85
86 def test_is_package(self):
87 self.assertFalse(self.loader.is_package(util.EXTENSIONS.name))
88 for suffix in self.machinery.EXTENSION_SUFFIXES:
89 path = os.path.join('some', 'path', 'pkg', '__init__' + suffix)
90 loader = self.machinery.ExtensionFileLoader('pkg', path)
91 self.assertTrue(loader.is_package('pkg'))
92
93
94 (Frozen_LoaderTests,
95 Source_LoaderTests
96 ) = util.test_both(LoaderTests, machinery=machinery)
97
98
99 class ESC[4;38;5;81mSinglePhaseExtensionModuleTests(ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mLoaderTests):
100 # Test loading extension modules without multi-phase initialization.
101
102 def setUp(self):
103 if not self.machinery.EXTENSION_SUFFIXES:
104 raise unittest.SkipTest("Requires dynamic loading support.")
105 self.name = '_testsinglephase'
106 if self.name in sys.builtin_module_names:
107 raise unittest.SkipTest(
108 f"{self.name} is a builtin module"
109 )
110 finder = self.machinery.FileFinder(None)
111 self.spec = importlib.util.find_spec(self.name)
112 assert self.spec
113 self.loader = self.machinery.ExtensionFileLoader(
114 self.name, self.spec.origin)
115
116 def load_module(self):
117 with warnings.catch_warnings():
118 warnings.simplefilter("ignore", DeprecationWarning)
119 return self.loader.load_module(self.name)
120
121 def load_module_by_name(self, fullname):
122 # Load a module from the test extension by name.
123 origin = self.spec.origin
124 loader = self.machinery.ExtensionFileLoader(fullname, origin)
125 spec = importlib.util.spec_from_loader(fullname, loader)
126 module = importlib.util.module_from_spec(spec)
127 loader.exec_module(module)
128 return module
129
130 def test_module(self):
131 # Test loading an extension module.
132 with util.uncache(self.name):
133 module = self.load_module()
134 for attr, value in [('__name__', self.name),
135 ('__file__', self.spec.origin),
136 ('__package__', '')]:
137 self.assertEqual(getattr(module, attr), value)
138 with self.assertRaises(AttributeError):
139 module.__path__
140 self.assertIs(module, sys.modules[self.name])
141 self.assertIsInstance(module.__loader__,
142 self.machinery.ExtensionFileLoader)
143
144 # No extension module as __init__ available for testing.
145 test_package = None
146
147 # No extension module in a package available for testing.
148 test_lacking_parent = None
149
150 # No easy way to trigger a failure after a successful import.
151 test_state_after_failure = None
152
153 def test_unloadable(self):
154 name = 'asdfjkl;'
155 with self.assertRaises(ImportError) as cm:
156 self.load_module_by_name(name)
157 self.assertEqual(cm.exception.name, name)
158
159 def test_unloadable_nonascii(self):
160 # Test behavior with nonexistent module with non-ASCII name.
161 name = 'fo\xf3'
162 with self.assertRaises(ImportError) as cm:
163 self.load_module_by_name(name)
164 self.assertEqual(cm.exception.name, name)
165
166 # It may make sense to add the equivalent to
167 # the following MultiPhaseExtensionModuleTests tests:
168 #
169 # * test_nonmodule
170 # * test_nonmodule_with_methods
171 # * test_bad_modules
172 # * test_nonascii
173
174
175 (Frozen_SinglePhaseExtensionModuleTests,
176 Source_SinglePhaseExtensionModuleTests
177 ) = util.test_both(SinglePhaseExtensionModuleTests, machinery=machinery)
178
179
180 class ESC[4;38;5;81mMultiPhaseExtensionModuleTests(ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mLoaderTests):
181 # Test loading extension modules with multi-phase initialization (PEP 489).
182
183 def setUp(self):
184 if not self.machinery.EXTENSION_SUFFIXES:
185 raise unittest.SkipTest("Requires dynamic loading support.")
186 self.name = '_testmultiphase'
187 if self.name in sys.builtin_module_names:
188 raise unittest.SkipTest(
189 f"{self.name} is a builtin module"
190 )
191 finder = self.machinery.FileFinder(None)
192 self.spec = importlib.util.find_spec(self.name)
193 assert self.spec
194 self.loader = self.machinery.ExtensionFileLoader(
195 self.name, self.spec.origin)
196
197 def load_module(self):
198 # Load the module from the test extension.
199 with warnings.catch_warnings():
200 warnings.simplefilter("ignore", DeprecationWarning)
201 return self.loader.load_module(self.name)
202
203 def load_module_by_name(self, fullname):
204 # Load a module from the test extension by name.
205 origin = self.spec.origin
206 loader = self.machinery.ExtensionFileLoader(fullname, origin)
207 spec = importlib.util.spec_from_loader(fullname, loader)
208 module = importlib.util.module_from_spec(spec)
209 loader.exec_module(module)
210 return module
211
212 # No extension module as __init__ available for testing.
213 test_package = None
214
215 # No extension module in a package available for testing.
216 test_lacking_parent = None
217
218 # Handling failure on reload is the up to the module.
219 test_state_after_failure = None
220
221 def test_module(self):
222 # Test loading an extension module.
223 with util.uncache(self.name):
224 module = self.load_module()
225 for attr, value in [('__name__', self.name),
226 ('__file__', self.spec.origin),
227 ('__package__', '')]:
228 self.assertEqual(getattr(module, attr), value)
229 with self.assertRaises(AttributeError):
230 module.__path__
231 self.assertIs(module, sys.modules[self.name])
232 self.assertIsInstance(module.__loader__,
233 self.machinery.ExtensionFileLoader)
234
235 def test_functionality(self):
236 # Test basic functionality of stuff defined in an extension module.
237 with util.uncache(self.name):
238 module = self.load_module()
239 self.assertIsInstance(module, types.ModuleType)
240 ex = module.Example()
241 self.assertEqual(ex.demo('abcd'), 'abcd')
242 self.assertEqual(ex.demo(), None)
243 with self.assertRaises(AttributeError):
244 ex.abc
245 ex.abc = 0
246 self.assertEqual(ex.abc, 0)
247 self.assertEqual(module.foo(9, 9), 18)
248 self.assertIsInstance(module.Str(), str)
249 self.assertEqual(module.Str(1) + '23', '123')
250 with self.assertRaises(module.error):
251 raise module.error()
252 self.assertEqual(module.int_const, 1969)
253 self.assertEqual(module.str_const, 'something different')
254
255 def test_reload(self):
256 # Test that reload didn't re-set the module's attributes.
257 with util.uncache(self.name):
258 module = self.load_module()
259 ex_class = module.Example
260 importlib.reload(module)
261 self.assertIs(ex_class, module.Example)
262
263 def test_try_registration(self):
264 # Assert that the PyState_{Find,Add,Remove}Module C API doesn't work.
265 with util.uncache(self.name):
266 module = self.load_module()
267 with self.subTest('PyState_FindModule'):
268 self.assertEqual(module.call_state_registration_func(0), None)
269 with self.subTest('PyState_AddModule'):
270 with self.assertRaises(SystemError):
271 module.call_state_registration_func(1)
272 with self.subTest('PyState_RemoveModule'):
273 with self.assertRaises(SystemError):
274 module.call_state_registration_func(2)
275
276 def test_load_submodule(self):
277 # Test loading a simulated submodule.
278 module = self.load_module_by_name('pkg.' + self.name)
279 self.assertIsInstance(module, types.ModuleType)
280 self.assertEqual(module.__name__, 'pkg.' + self.name)
281 self.assertEqual(module.str_const, 'something different')
282
283 def test_load_short_name(self):
284 # Test loading module with a one-character name.
285 module = self.load_module_by_name('x')
286 self.assertIsInstance(module, types.ModuleType)
287 self.assertEqual(module.__name__, 'x')
288 self.assertEqual(module.str_const, 'something different')
289 self.assertNotIn('x', sys.modules)
290
291 def test_load_twice(self):
292 # Test that 2 loads result in 2 module objects.
293 module1 = self.load_module_by_name(self.name)
294 module2 = self.load_module_by_name(self.name)
295 self.assertIsNot(module1, module2)
296
297 def test_unloadable(self):
298 # Test nonexistent module.
299 name = 'asdfjkl;'
300 with self.assertRaises(ImportError) as cm:
301 self.load_module_by_name(name)
302 self.assertEqual(cm.exception.name, name)
303
304 def test_unloadable_nonascii(self):
305 # Test behavior with nonexistent module with non-ASCII name.
306 name = 'fo\xf3'
307 with self.assertRaises(ImportError) as cm:
308 self.load_module_by_name(name)
309 self.assertEqual(cm.exception.name, name)
310
311 def test_nonmodule(self):
312 # Test returning a non-module object from create works.
313 name = self.name + '_nonmodule'
314 mod = self.load_module_by_name(name)
315 self.assertNotEqual(type(mod), type(unittest))
316 self.assertEqual(mod.three, 3)
317
318 # issue 27782
319 def test_nonmodule_with_methods(self):
320 # Test creating a non-module object with methods defined.
321 name = self.name + '_nonmodule_with_methods'
322 mod = self.load_module_by_name(name)
323 self.assertNotEqual(type(mod), type(unittest))
324 self.assertEqual(mod.three, 3)
325 self.assertEqual(mod.bar(10, 1), 9)
326
327 def test_null_slots(self):
328 # Test that NULL slots aren't a problem.
329 name = self.name + '_null_slots'
330 module = self.load_module_by_name(name)
331 self.assertIsInstance(module, types.ModuleType)
332 self.assertEqual(module.__name__, name)
333
334 def test_bad_modules(self):
335 # Test SystemError is raised for misbehaving extensions.
336 for name_base in [
337 'bad_slot_large',
338 'bad_slot_negative',
339 'create_int_with_state',
340 'negative_size',
341 'export_null',
342 'export_uninitialized',
343 'export_raise',
344 'export_unreported_exception',
345 'create_null',
346 'create_raise',
347 'create_unreported_exception',
348 'nonmodule_with_exec_slots',
349 'exec_err',
350 'exec_raise',
351 'exec_unreported_exception',
352 'multiple_create_slots',
353 'multiple_multiple_interpreters_slots',
354 ]:
355 with self.subTest(name_base):
356 name = self.name + '_' + name_base
357 with self.assertRaises(SystemError) as cm:
358 self.load_module_by_name(name)
359
360 # If there is an unreported exception, it should be chained
361 # with the `SystemError`.
362 if "unreported_exception" in name_base:
363 self.assertIsNotNone(cm.exception.__cause__)
364
365 def test_nonascii(self):
366 # Test that modules with non-ASCII names can be loaded.
367 # punycode behaves slightly differently in some-ASCII and no-ASCII
368 # cases, so test both.
369 cases = [
370 (self.name + '_zkou\u0161ka_na\u010dten\xed', 'Czech'),
371 ('\uff3f\u30a4\u30f3\u30dd\u30fc\u30c8\u30c6\u30b9\u30c8',
372 'Japanese'),
373 ]
374 for name, lang in cases:
375 with self.subTest(name):
376 module = self.load_module_by_name(name)
377 self.assertEqual(module.__name__, name)
378 self.assertEqual(module.__doc__, "Module named in %s" % lang)
379
380
381 (Frozen_MultiPhaseExtensionModuleTests,
382 Source_MultiPhaseExtensionModuleTests
383 ) = util.test_both(MultiPhaseExtensionModuleTests, machinery=machinery)
384
385
386 if __name__ == '__main__':
387 unittest.main()