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