1 import gc
2 import importlib
3 import importlib.util
4 import os
5 import os.path
6 import py_compile
7 import sys
8 from test import support
9 from test.support import import_helper
10 from test.support import os_helper
11 from test.support import script_helper
12 from test.support import warnings_helper
13 import unittest
14 import warnings
15 imp = warnings_helper.import_deprecated('imp')
16 import _imp
17
18
19 OS_PATH_NAME = os.path.__name__
20
21
22 def requires_load_dynamic(meth):
23 """Decorator to skip a test if not running under CPython or lacking
24 imp.load_dynamic()."""
25 meth = support.cpython_only(meth)
26 return unittest.skipIf(getattr(imp, 'load_dynamic', None) is None,
27 'imp.load_dynamic() required')(meth)
28
29
30 class ESC[4;38;5;81mLockTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
31
32 """Very basic test of import lock functions."""
33
34 def verify_lock_state(self, expected):
35 self.assertEqual(imp.lock_held(), expected,
36 "expected imp.lock_held() to be %r" % expected)
37 def testLock(self):
38 LOOPS = 50
39
40 # The import lock may already be held, e.g. if the test suite is run
41 # via "import test.autotest".
42 lock_held_at_start = imp.lock_held()
43 self.verify_lock_state(lock_held_at_start)
44
45 for i in range(LOOPS):
46 imp.acquire_lock()
47 self.verify_lock_state(True)
48
49 for i in range(LOOPS):
50 imp.release_lock()
51
52 # The original state should be restored now.
53 self.verify_lock_state(lock_held_at_start)
54
55 if not lock_held_at_start:
56 try:
57 imp.release_lock()
58 except RuntimeError:
59 pass
60 else:
61 self.fail("release_lock() without lock should raise "
62 "RuntimeError")
63
64 class ESC[4;38;5;81mImportTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
65 def setUp(self):
66 mod = importlib.import_module('test.encoded_modules')
67 self.test_strings = mod.test_strings
68 self.test_path = mod.__path__
69
70 def test_import_encoded_module(self):
71 for modname, encoding, teststr in self.test_strings:
72 mod = importlib.import_module('test.encoded_modules.'
73 'module_' + modname)
74 self.assertEqual(teststr, mod.test)
75
76 def test_find_module_encoding(self):
77 for mod, encoding, _ in self.test_strings:
78 with imp.find_module('module_' + mod, self.test_path)[0] as fd:
79 self.assertEqual(fd.encoding, encoding)
80
81 path = [os.path.join(os.path.dirname(__file__), 'tokenizedata')]
82 with self.assertRaises(SyntaxError):
83 imp.find_module('badsyntax_pep3120', path)
84
85 def test_issue1267(self):
86 for mod, encoding, _ in self.test_strings:
87 fp, filename, info = imp.find_module('module_' + mod,
88 self.test_path)
89 with fp:
90 self.assertNotEqual(fp, None)
91 self.assertEqual(fp.encoding, encoding)
92 self.assertEqual(fp.tell(), 0)
93 self.assertEqual(fp.readline(), '# test %s encoding\n'
94 % encoding)
95
96 fp, filename, info = imp.find_module("tokenize")
97 with fp:
98 self.assertNotEqual(fp, None)
99 self.assertEqual(fp.encoding, "utf-8")
100 self.assertEqual(fp.tell(), 0)
101 self.assertEqual(fp.readline(),
102 '"""Tokenization help for Python programs.\n')
103
104 def test_issue3594(self):
105 temp_mod_name = 'test_imp_helper'
106 sys.path.insert(0, '.')
107 try:
108 with open(temp_mod_name + '.py', 'w', encoding="latin-1") as file:
109 file.write("# coding: cp1252\nu = 'test.test_imp'\n")
110 file, filename, info = imp.find_module(temp_mod_name)
111 file.close()
112 self.assertEqual(file.encoding, 'cp1252')
113 finally:
114 del sys.path[0]
115 os_helper.unlink(temp_mod_name + '.py')
116 os_helper.unlink(temp_mod_name + '.pyc')
117
118 def test_issue5604(self):
119 # Test cannot cover imp.load_compiled function.
120 # Martin von Loewis note what shared library cannot have non-ascii
121 # character because init_xxx function cannot be compiled
122 # and issue never happens for dynamic modules.
123 # But sources modified to follow generic way for processing paths.
124
125 # the return encoding could be uppercase or None
126 fs_encoding = sys.getfilesystemencoding()
127
128 # covers utf-8 and Windows ANSI code pages
129 # one non-space symbol from every page
130 # (http://en.wikipedia.org/wiki/Code_page)
131 known_locales = {
132 'utf-8' : b'\xc3\xa4',
133 'cp1250' : b'\x8C',
134 'cp1251' : b'\xc0',
135 'cp1252' : b'\xc0',
136 'cp1253' : b'\xc1',
137 'cp1254' : b'\xc0',
138 'cp1255' : b'\xe0',
139 'cp1256' : b'\xe0',
140 'cp1257' : b'\xc0',
141 'cp1258' : b'\xc0',
142 }
143
144 if sys.platform == 'darwin':
145 self.assertEqual(fs_encoding, 'utf-8')
146 # Mac OS X uses the Normal Form D decomposition
147 # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html
148 special_char = b'a\xcc\x88'
149 else:
150 special_char = known_locales.get(fs_encoding)
151
152 if not special_char:
153 self.skipTest("can't run this test with %s as filesystem encoding"
154 % fs_encoding)
155 decoded_char = special_char.decode(fs_encoding)
156 temp_mod_name = 'test_imp_helper_' + decoded_char
157 test_package_name = 'test_imp_helper_package_' + decoded_char
158 init_file_name = os.path.join(test_package_name, '__init__.py')
159 try:
160 # if the curdir is not in sys.path the test fails when run with
161 # ./python ./Lib/test/regrtest.py test_imp
162 sys.path.insert(0, os.curdir)
163 with open(temp_mod_name + '.py', 'w', encoding="utf-8") as file:
164 file.write('a = 1\n')
165 file, filename, info = imp.find_module(temp_mod_name)
166 with file:
167 self.assertIsNotNone(file)
168 self.assertTrue(filename[:-3].endswith(temp_mod_name))
169 self.assertEqual(info[0], '.py')
170 self.assertEqual(info[1], 'r')
171 self.assertEqual(info[2], imp.PY_SOURCE)
172
173 mod = imp.load_module(temp_mod_name, file, filename, info)
174 self.assertEqual(mod.a, 1)
175
176 with warnings.catch_warnings():
177 warnings.simplefilter('ignore')
178 mod = imp.load_source(temp_mod_name, temp_mod_name + '.py')
179 self.assertEqual(mod.a, 1)
180
181 with warnings.catch_warnings():
182 warnings.simplefilter('ignore')
183 if not sys.dont_write_bytecode:
184 mod = imp.load_compiled(
185 temp_mod_name,
186 imp.cache_from_source(temp_mod_name + '.py'))
187 self.assertEqual(mod.a, 1)
188
189 if not os.path.exists(test_package_name):
190 os.mkdir(test_package_name)
191 with open(init_file_name, 'w', encoding="utf-8") as file:
192 file.write('b = 2\n')
193 with warnings.catch_warnings():
194 warnings.simplefilter('ignore')
195 package = imp.load_package(test_package_name, test_package_name)
196 self.assertEqual(package.b, 2)
197 finally:
198 del sys.path[0]
199 for ext in ('.py', '.pyc'):
200 os_helper.unlink(temp_mod_name + ext)
201 os_helper.unlink(init_file_name + ext)
202 os_helper.rmtree(test_package_name)
203 os_helper.rmtree('__pycache__')
204
205 def test_issue9319(self):
206 path = os.path.join(os.path.dirname(__file__), "tokenizedata")
207 self.assertRaises(SyntaxError,
208 imp.find_module,
209 "badsyntax_pep3120",
210 [path])
211
212 def test_load_from_source(self):
213 # Verify that the imp module can correctly load and find .py files
214 # XXX (ncoghlan): It would be nice to use import_helper.CleanImport
215 # here, but that breaks because the os module registers some
216 # handlers in copy_reg on import. Since CleanImport doesn't
217 # revert that registration, the module is left in a broken
218 # state after reversion. Reinitialising the module contents
219 # and just reverting os.environ to its previous state is an OK
220 # workaround
221 with import_helper.CleanImport('os', 'os.path', OS_PATH_NAME):
222 import os
223 orig_path = os.path
224 orig_getenv = os.getenv
225 with os_helper.EnvironmentVarGuard():
226 x = imp.find_module("os")
227 self.addCleanup(x[0].close)
228 new_os = imp.load_module("os", *x)
229 self.assertIs(os, new_os)
230 self.assertIs(orig_path, new_os.path)
231 self.assertIsNot(orig_getenv, new_os.getenv)
232
233 @requires_load_dynamic
234 def test_issue15828_load_extensions(self):
235 # Issue 15828 picked up that the adapter between the old imp API
236 # and importlib couldn't handle C extensions
237 example = "_heapq"
238 x = imp.find_module(example)
239 file_ = x[0]
240 if file_ is not None:
241 self.addCleanup(file_.close)
242 mod = imp.load_module(example, *x)
243 self.assertEqual(mod.__name__, example)
244
245 @requires_load_dynamic
246 def test_issue16421_multiple_modules_in_one_dll(self):
247 # Issue 16421: loading several modules from the same compiled file fails
248 m = '_testimportmultiple'
249 fileobj, pathname, description = imp.find_module(m)
250 fileobj.close()
251 mod0 = imp.load_dynamic(m, pathname)
252 mod1 = imp.load_dynamic('_testimportmultiple_foo', pathname)
253 mod2 = imp.load_dynamic('_testimportmultiple_bar', pathname)
254 self.assertEqual(mod0.__name__, m)
255 self.assertEqual(mod1.__name__, '_testimportmultiple_foo')
256 self.assertEqual(mod2.__name__, '_testimportmultiple_bar')
257 with self.assertRaises(ImportError):
258 imp.load_dynamic('nonexistent', pathname)
259
260 @requires_load_dynamic
261 def test_load_dynamic_ImportError_path(self):
262 # Issue #1559549 added `name` and `path` attributes to ImportError
263 # in order to provide better detail. Issue #10854 implemented those
264 # attributes on import failures of extensions on Windows.
265 path = 'bogus file path'
266 name = 'extension'
267 with self.assertRaises(ImportError) as err:
268 imp.load_dynamic(name, path)
269 self.assertIn(path, err.exception.path)
270 self.assertEqual(name, err.exception.name)
271
272 @requires_load_dynamic
273 def test_load_module_extension_file_is_None(self):
274 # When loading an extension module and the file is None, open one
275 # on the behalf of imp.load_dynamic().
276 # Issue #15902
277 name = '_testimportmultiple'
278 found = imp.find_module(name)
279 if found[0] is not None:
280 found[0].close()
281 if found[2][2] != imp.C_EXTENSION:
282 self.skipTest("found module doesn't appear to be a C extension")
283 imp.load_module(name, None, *found[1:])
284
285 @requires_load_dynamic
286 def test_issue24748_load_module_skips_sys_modules_check(self):
287 name = 'test.imp_dummy'
288 try:
289 del sys.modules[name]
290 except KeyError:
291 pass
292 try:
293 module = importlib.import_module(name)
294 spec = importlib.util.find_spec('_testmultiphase')
295 module = imp.load_dynamic(name, spec.origin)
296 self.assertEqual(module.__name__, name)
297 self.assertEqual(module.__spec__.name, name)
298 self.assertEqual(module.__spec__.origin, spec.origin)
299 self.assertRaises(AttributeError, getattr, module, 'dummy_name')
300 self.assertEqual(module.int_const, 1969)
301 self.assertIs(sys.modules[name], module)
302 finally:
303 try:
304 del sys.modules[name]
305 except KeyError:
306 pass
307
308 @unittest.skipIf(sys.dont_write_bytecode,
309 "test meaningful only when writing bytecode")
310 def test_bug7732(self):
311 with os_helper.temp_cwd():
312 source = os_helper.TESTFN + '.py'
313 os.mkdir(source)
314 self.assertRaisesRegex(ImportError, '^No module',
315 imp.find_module, os_helper.TESTFN, ["."])
316
317 def test_multiple_calls_to_get_data(self):
318 # Issue #18755: make sure multiple calls to get_data() can succeed.
319 loader = imp._LoadSourceCompatibility('imp', imp.__file__,
320 open(imp.__file__, encoding="utf-8"))
321 loader.get_data(imp.__file__) # File should be closed
322 loader.get_data(imp.__file__) # Will need to create a newly opened file
323
324 def test_load_source(self):
325 # Create a temporary module since load_source(name) modifies
326 # sys.modules[name] attributes like __loader___
327 modname = f"tmp{__name__}"
328 mod = type(sys.modules[__name__])(modname)
329 with support.swap_item(sys.modules, modname, mod):
330 with self.assertRaisesRegex(ValueError, 'embedded null'):
331 imp.load_source(modname, __file__ + "\0")
332
333 @support.cpython_only
334 def test_issue31315(self):
335 # There shouldn't be an assertion failure in imp.create_dynamic(),
336 # when spec.name is not a string.
337 create_dynamic = support.get_attribute(imp, 'create_dynamic')
338 class ESC[4;38;5;81mBadSpec:
339 name = None
340 origin = 'foo'
341 with self.assertRaises(TypeError):
342 create_dynamic(BadSpec())
343
344 def test_issue_35321(self):
345 # Both _frozen_importlib and _frozen_importlib_external
346 # should have a spec origin of "frozen" and
347 # no need to clean up imports in this case.
348
349 import _frozen_importlib_external
350 self.assertEqual(_frozen_importlib_external.__spec__.origin, "frozen")
351
352 import _frozen_importlib
353 self.assertEqual(_frozen_importlib.__spec__.origin, "frozen")
354
355 def test_source_hash(self):
356 self.assertEqual(_imp.source_hash(42, b'hi'), b'\xfb\xd9G\x05\xaf$\x9b~')
357 self.assertEqual(_imp.source_hash(43, b'hi'), b'\xd0/\x87C\xccC\xff\xe2')
358
359 def test_pyc_invalidation_mode_from_cmdline(self):
360 cases = [
361 ([], "default"),
362 (["--check-hash-based-pycs", "default"], "default"),
363 (["--check-hash-based-pycs", "always"], "always"),
364 (["--check-hash-based-pycs", "never"], "never"),
365 ]
366 for interp_args, expected in cases:
367 args = interp_args + [
368 "-c",
369 "import _imp; print(_imp.check_hash_based_pycs)",
370 ]
371 res = script_helper.assert_python_ok(*args)
372 self.assertEqual(res.out.strip().decode('utf-8'), expected)
373
374 def test_find_and_load_checked_pyc(self):
375 # issue 34056
376 with os_helper.temp_cwd():
377 with open('mymod.py', 'wb') as fp:
378 fp.write(b'x = 42\n')
379 py_compile.compile(
380 'mymod.py',
381 doraise=True,
382 invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
383 )
384 file, path, description = imp.find_module('mymod', path=['.'])
385 mod = imp.load_module('mymod', file, path, description)
386 self.assertEqual(mod.x, 42)
387
388
389 @support.cpython_only
390 def test_create_builtin_subinterp(self):
391 # gh-99578: create_builtin() behavior changes after the creation of the
392 # first sub-interpreter. Test both code paths, before and after the
393 # creation of a sub-interpreter. Previously, create_builtin() had
394 # a reference leak after the creation of the first sub-interpreter.
395
396 import builtins
397 create_builtin = support.get_attribute(_imp, "create_builtin")
398 class ESC[4;38;5;81mSpec:
399 name = "builtins"
400 spec = Spec()
401
402 def check_get_builtins():
403 refcnt = sys.getrefcount(builtins)
404 mod = _imp.create_builtin(spec)
405 self.assertIs(mod, builtins)
406 self.assertEqual(sys.getrefcount(builtins), refcnt + 1)
407 # Check that a GC collection doesn't crash
408 gc.collect()
409
410 check_get_builtins()
411
412 ret = support.run_in_subinterp("import builtins")
413 self.assertEqual(ret, 0)
414
415 check_get_builtins()
416
417
418 class ESC[4;38;5;81mReloadTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
419
420 """Very basic tests to make sure that imp.reload() operates just like
421 reload()."""
422
423 def test_source(self):
424 # XXX (ncoghlan): It would be nice to use test.import_helper.CleanImport
425 # here, but that breaks because the os module registers some
426 # handlers in copy_reg on import. Since CleanImport doesn't
427 # revert that registration, the module is left in a broken
428 # state after reversion. Reinitialising the module contents
429 # and just reverting os.environ to its previous state is an OK
430 # workaround
431 with os_helper.EnvironmentVarGuard():
432 import os
433 imp.reload(os)
434
435 def test_extension(self):
436 with import_helper.CleanImport('time'):
437 import time
438 imp.reload(time)
439
440 def test_builtin(self):
441 with import_helper.CleanImport('marshal'):
442 import marshal
443 imp.reload(marshal)
444
445 def test_with_deleted_parent(self):
446 # see #18681
447 from html import parser
448 html = sys.modules.pop('html')
449 def cleanup():
450 sys.modules['html'] = html
451 self.addCleanup(cleanup)
452 with self.assertRaisesRegex(ImportError, 'html'):
453 imp.reload(parser)
454
455
456 class ESC[4;38;5;81mPEP3147Tests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
457 """Tests of PEP 3147."""
458
459 tag = imp.get_tag()
460
461 @unittest.skipUnless(sys.implementation.cache_tag is not None,
462 'requires sys.implementation.cache_tag not be None')
463 def test_cache_from_source(self):
464 # Given the path to a .py file, return the path to its PEP 3147
465 # defined .pyc file (i.e. under __pycache__).
466 path = os.path.join('foo', 'bar', 'baz', 'qux.py')
467 expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
468 'qux.{}.pyc'.format(self.tag))
469 self.assertEqual(imp.cache_from_source(path, True), expect)
470
471 @unittest.skipUnless(sys.implementation.cache_tag is not None,
472 'requires sys.implementation.cache_tag to not be '
473 'None')
474 def test_source_from_cache(self):
475 # Given the path to a PEP 3147 defined .pyc file, return the path to
476 # its source. This tests the good path.
477 path = os.path.join('foo', 'bar', 'baz', '__pycache__',
478 'qux.{}.pyc'.format(self.tag))
479 expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
480 self.assertEqual(imp.source_from_cache(path), expect)
481
482
483 class ESC[4;38;5;81mNullImporterTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
484 @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None,
485 "Need an undecodeable filename")
486 def test_unencodeable(self):
487 name = os_helper.TESTFN_UNENCODABLE
488 os.mkdir(name)
489 try:
490 self.assertRaises(ImportError, imp.NullImporter, name)
491 finally:
492 os.rmdir(name)
493
494
495 if __name__ == "__main__":
496 unittest.main()