1from warnings import catch_warnings 2from .. import abc 3from .. import util 4 5machinery = util.import_importlib('importlib.machinery') 6 7import os.path 8import sys 9import types 10import unittest 11import warnings 12import importlib.util 13import importlib 14from test.support.script_helper import assert_python_failure 15 16class LoaderTests(abc.LoaderTests): 17 18 """Test load_module() for extension modules.""" 19 20 def setUp(self): 21 self.loader = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name, 22 util.EXTENSIONS.file_path) 23 24 def load_module(self, fullname): 25 with warnings.catch_warnings(): 26 warnings.simplefilter("ignore", DeprecationWarning) 27 return self.loader.load_module(fullname) 28 29 def test_load_module_API(self): 30 # Test the default argument for load_module(). 31 with warnings.catch_warnings(): 32 warnings.simplefilter("ignore", DeprecationWarning) 33 self.loader.load_module() 34 self.loader.load_module(None) 35 with self.assertRaises(ImportError): 36 self.load_module('XXX') 37 38 def test_equality(self): 39 other = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name, 40 util.EXTENSIONS.file_path) 41 self.assertEqual(self.loader, other) 42 43 def test_inequality(self): 44 other = self.machinery.ExtensionFileLoader('_' + util.EXTENSIONS.name, 45 util.EXTENSIONS.file_path) 46 self.assertNotEqual(self.loader, other) 47 48 def test_module(self): 49 with util.uncache(util.EXTENSIONS.name): 50 module = self.load_module(util.EXTENSIONS.name) 51 for attr, value in [('__name__', util.EXTENSIONS.name), 52 ('__file__', util.EXTENSIONS.file_path), 53 ('__package__', '')]: 54 self.assertEqual(getattr(module, attr), value) 55 self.assertIn(util.EXTENSIONS.name, sys.modules) 56 self.assertIsInstance(module.__loader__, 57 self.machinery.ExtensionFileLoader) 58 59 # No extension module as __init__ available for testing. 60 test_package = None 61 62 # No extension module in a package available for testing. 63 test_lacking_parent = None 64 65 def test_module_reuse(self): 66 with util.uncache(util.EXTENSIONS.name): 67 module1 = self.load_module(util.EXTENSIONS.name) 68 module2 = self.load_module(util.EXTENSIONS.name) 69 self.assertIs(module1, module2) 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_is_package(self): 81 self.assertFalse(self.loader.is_package(util.EXTENSIONS.name)) 82 for suffix in self.machinery.EXTENSION_SUFFIXES: 83 path = os.path.join('some', 'path', 'pkg', '__init__' + suffix) 84 loader = self.machinery.ExtensionFileLoader('pkg', path) 85 self.assertTrue(loader.is_package('pkg')) 86 87(Frozen_LoaderTests, 88 Source_LoaderTests 89 ) = util.test_both(LoaderTests, machinery=machinery) 90 91class MultiPhaseExtensionModuleTests(abc.LoaderTests): 92 # Test loading extension modules with multi-phase initialization (PEP 489). 93 94 def setUp(self): 95 self.name = '_testmultiphase' 96 finder = self.machinery.FileFinder(None) 97 self.spec = importlib.util.find_spec(self.name) 98 assert self.spec 99 self.loader = self.machinery.ExtensionFileLoader( 100 self.name, self.spec.origin) 101 102 def load_module(self): 103 # Load the module from the test extension. 104 with warnings.catch_warnings(): 105 warnings.simplefilter("ignore", DeprecationWarning) 106 return self.loader.load_module(self.name) 107 108 def load_module_by_name(self, fullname): 109 # Load a module from the test extension by name. 110 origin = self.spec.origin 111 loader = self.machinery.ExtensionFileLoader(fullname, origin) 112 spec = importlib.util.spec_from_loader(fullname, loader) 113 module = importlib.util.module_from_spec(spec) 114 loader.exec_module(module) 115 return module 116 117 # No extension module as __init__ available for testing. 118 test_package = None 119 120 # No extension module in a package available for testing. 121 test_lacking_parent = None 122 123 # Handling failure on reload is the up to the module. 124 test_state_after_failure = None 125 126 def test_module(self): 127 # Test loading an extension module. 128 with util.uncache(self.name): 129 module = self.load_module() 130 for attr, value in [('__name__', self.name), 131 ('__file__', self.spec.origin), 132 ('__package__', '')]: 133 self.assertEqual(getattr(module, attr), value) 134 with self.assertRaises(AttributeError): 135 module.__path__ 136 self.assertIs(module, sys.modules[self.name]) 137 self.assertIsInstance(module.__loader__, 138 self.machinery.ExtensionFileLoader) 139 140 def test_functionality(self): 141 # Test basic functionality of stuff defined in an extension module. 142 with util.uncache(self.name): 143 module = self.load_module() 144 self.assertIsInstance(module, types.ModuleType) 145 ex = module.Example() 146 self.assertEqual(ex.demo('abcd'), 'abcd') 147 self.assertEqual(ex.demo(), None) 148 with self.assertRaises(AttributeError): 149 ex.abc 150 ex.abc = 0 151 self.assertEqual(ex.abc, 0) 152 self.assertEqual(module.foo(9, 9), 18) 153 self.assertIsInstance(module.Str(), str) 154 self.assertEqual(module.Str(1) + '23', '123') 155 with self.assertRaises(module.error): 156 raise module.error() 157 self.assertEqual(module.int_const, 1969) 158 self.assertEqual(module.str_const, 'something different') 159 160 def test_reload(self): 161 # Test that reload didn't re-set the module's attributes. 162 with util.uncache(self.name): 163 module = self.load_module() 164 ex_class = module.Example 165 importlib.reload(module) 166 self.assertIs(ex_class, module.Example) 167 168 def test_try_registration(self): 169 # Assert that the PyState_{Find,Add,Remove}Module C API doesn't work. 170 module = self.load_module() 171 with self.subTest('PyState_FindModule'): 172 self.assertEqual(module.call_state_registration_func(0), None) 173 with self.subTest('PyState_AddModule'): 174 with self.assertRaises(SystemError): 175 module.call_state_registration_func(1) 176 with self.subTest('PyState_RemoveModule'): 177 with self.assertRaises(SystemError): 178 module.call_state_registration_func(2) 179 180 def test_load_submodule(self): 181 # Test loading a simulated submodule. 182 module = self.load_module_by_name('pkg.' + self.name) 183 self.assertIsInstance(module, types.ModuleType) 184 self.assertEqual(module.__name__, 'pkg.' + self.name) 185 self.assertEqual(module.str_const, 'something different') 186 187 def test_load_short_name(self): 188 # Test loading module with a one-character name. 189 module = self.load_module_by_name('x') 190 self.assertIsInstance(module, types.ModuleType) 191 self.assertEqual(module.__name__, 'x') 192 self.assertEqual(module.str_const, 'something different') 193 self.assertNotIn('x', sys.modules) 194 195 def test_load_twice(self): 196 # Test that 2 loads result in 2 module objects. 197 module1 = self.load_module_by_name(self.name) 198 module2 = self.load_module_by_name(self.name) 199 self.assertIsNot(module1, module2) 200 201 def test_unloadable(self): 202 # Test nonexistent module. 203 name = 'asdfjkl;' 204 with self.assertRaises(ImportError) as cm: 205 self.load_module_by_name(name) 206 self.assertEqual(cm.exception.name, name) 207 208 def test_unloadable_nonascii(self): 209 # Test behavior with nonexistent module with non-ASCII name. 210 name = 'fo\xf3' 211 with self.assertRaises(ImportError) as cm: 212 self.load_module_by_name(name) 213 self.assertEqual(cm.exception.name, name) 214 215 def test_nonmodule(self): 216 # Test returning a non-module object from create works. 217 name = self.name + '_nonmodule' 218 mod = self.load_module_by_name(name) 219 self.assertNotEqual(type(mod), type(unittest)) 220 self.assertEqual(mod.three, 3) 221 222 # issue 27782 223 def test_nonmodule_with_methods(self): 224 # Test creating a non-module object with methods defined. 225 name = self.name + '_nonmodule_with_methods' 226 mod = self.load_module_by_name(name) 227 self.assertNotEqual(type(mod), type(unittest)) 228 self.assertEqual(mod.three, 3) 229 self.assertEqual(mod.bar(10, 1), 9) 230 231 def test_null_slots(self): 232 # Test that NULL slots aren't a problem. 233 name = self.name + '_null_slots' 234 module = self.load_module_by_name(name) 235 self.assertIsInstance(module, types.ModuleType) 236 self.assertEqual(module.__name__, name) 237 238 def test_bad_modules(self): 239 # Test SystemError is raised for misbehaving extensions. 240 for name_base in [ 241 'bad_slot_large', 242 'bad_slot_negative', 243 'create_int_with_state', 244 'negative_size', 245 'export_null', 246 'export_uninitialized', 247 'export_raise', 248 'export_unreported_exception', 249 'create_null', 250 'create_raise', 251 'create_unreported_exception', 252 'nonmodule_with_exec_slots', 253 'exec_err', 254 'exec_raise', 255 'exec_unreported_exception', 256 ]: 257 with self.subTest(name_base): 258 name = self.name + '_' + name_base 259 with self.assertRaises(SystemError): 260 self.load_module_by_name(name) 261 262 def test_nonascii(self): 263 # Test that modules with non-ASCII names can be loaded. 264 # punycode behaves slightly differently in some-ASCII and no-ASCII 265 # cases, so test both. 266 cases = [ 267 (self.name + '_zkou\u0161ka_na\u010dten\xed', 'Czech'), 268 ('\uff3f\u30a4\u30f3\u30dd\u30fc\u30c8\u30c6\u30b9\u30c8', 269 'Japanese'), 270 ] 271 for name, lang in cases: 272 with self.subTest(name): 273 module = self.load_module_by_name(name) 274 self.assertEqual(module.__name__, name) 275 self.assertEqual(module.__doc__, "Module named in %s" % lang) 276 277 278(Frozen_MultiPhaseExtensionModuleTests, 279 Source_MultiPhaseExtensionModuleTests 280 ) = util.test_both(MultiPhaseExtensionModuleTests, machinery=machinery) 281 282 283if __name__ == '__main__': 284 unittest.main() 285