|  | 
|  | 1 | +import importlib.util | 
|  | 2 | +import os.path | 
|  | 3 | +import sys | 
|  | 4 | +import types | 
|  | 5 | +import unittest | 
|  | 6 | +from test.support import os_helper | 
|  | 7 | +from test.support import import_helper | 
|  | 8 | +from test.support.warnings_helper import check_warnings | 
|  | 9 | + | 
|  | 10 | +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') | 
|  | 11 | +NULL = None | 
|  | 12 | + | 
|  | 13 | + | 
|  | 14 | +class ImportTests(unittest.TestCase): | 
|  | 15 | +    def test_getmagicnumber(self): | 
|  | 16 | +        # Test PyImport_GetMagicNumber() | 
|  | 17 | +        magic = _testlimitedcapi.PyImport_GetMagicNumber() | 
|  | 18 | +        self.assertEqual(magic, | 
|  | 19 | +                         int.from_bytes(importlib.util.MAGIC_NUMBER, 'little')) | 
|  | 20 | + | 
|  | 21 | +    def test_getmagictag(self): | 
|  | 22 | +        # Test PyImport_GetMagicTag() | 
|  | 23 | +        tag = _testlimitedcapi.PyImport_GetMagicTag() | 
|  | 24 | +        self.assertEqual(tag, sys.implementation.cache_tag) | 
|  | 25 | + | 
|  | 26 | +    def test_getmoduledict(self): | 
|  | 27 | +        # Test PyImport_GetModuleDict() | 
|  | 28 | +        modules = _testlimitedcapi.PyImport_GetModuleDict() | 
|  | 29 | +        self.assertIs(modules, sys.modules) | 
|  | 30 | + | 
|  | 31 | +    def check_import_loaded_module(self, import_module): | 
|  | 32 | +        for name in ('os', 'sys', 'test', 'unittest'): | 
|  | 33 | +            with self.subTest(name=name): | 
|  | 34 | +                self.assertIn(name, sys.modules) | 
|  | 35 | +                old_module = sys.modules[name] | 
|  | 36 | +                module = import_module(name) | 
|  | 37 | +                self.assertIsInstance(module, types.ModuleType) | 
|  | 38 | +                self.assertIs(module, old_module) | 
|  | 39 | + | 
|  | 40 | +    def check_import_fresh_module(self, import_module): | 
|  | 41 | +        old_modules = dict(sys.modules) | 
|  | 42 | +        try: | 
|  | 43 | +            for name in ('colorsys', 'math'): | 
|  | 44 | +                with self.subTest(name=name): | 
|  | 45 | +                    sys.modules.pop(name, None) | 
|  | 46 | +                    module = import_module(name) | 
|  | 47 | +                    self.assertIsInstance(module, types.ModuleType) | 
|  | 48 | +                    self.assertIs(module, sys.modules[name]) | 
|  | 49 | +                    self.assertEqual(module.__name__, name) | 
|  | 50 | +        finally: | 
|  | 51 | +            sys.modules.clear() | 
|  | 52 | +            sys.modules.update(old_modules) | 
|  | 53 | + | 
|  | 54 | +    def test_getmodule(self): | 
|  | 55 | +        # Test PyImport_GetModule() | 
|  | 56 | +        getmodule = _testlimitedcapi.PyImport_GetModule | 
|  | 57 | +        self.check_import_loaded_module(getmodule) | 
|  | 58 | + | 
|  | 59 | +        nonexistent = 'nonexistent' | 
|  | 60 | +        self.assertNotIn(nonexistent, sys.modules) | 
|  | 61 | +        self.assertIs(getmodule(nonexistent), KeyError) | 
|  | 62 | +        self.assertIs(getmodule(''), KeyError) | 
|  | 63 | +        self.assertIs(getmodule(object()), KeyError) | 
|  | 64 | + | 
|  | 65 | +        self.assertRaises(TypeError, getmodule, [])  # unhashable | 
|  | 66 | +        # CRASHES getmodule(NULL) | 
|  | 67 | + | 
|  | 68 | +    def check_addmodule(self, add_module, accept_nonstr=False): | 
|  | 69 | +        # create a new module | 
|  | 70 | +        names = ['nonexistent'] | 
|  | 71 | +        if accept_nonstr: | 
|  | 72 | +            names.append(b'\xff')  # non-UTF-8 | 
|  | 73 | +        for name in names: | 
|  | 74 | +            with self.subTest(name=name): | 
|  | 75 | +                self.assertNotIn(name, sys.modules) | 
|  | 76 | +                try: | 
|  | 77 | +                    module = add_module(name) | 
|  | 78 | +                    self.assertIsInstance(module, types.ModuleType) | 
|  | 79 | +                    self.assertEqual(module.__name__, name) | 
|  | 80 | +                    self.assertIs(module, sys.modules[name]) | 
|  | 81 | +                finally: | 
|  | 82 | +                    sys.modules.pop(name, None) | 
|  | 83 | + | 
|  | 84 | +        # get an existing module | 
|  | 85 | +        self.check_import_loaded_module(add_module) | 
|  | 86 | + | 
|  | 87 | +    def test_addmoduleobject(self): | 
|  | 88 | +        # Test PyImport_AddModuleObject() | 
|  | 89 | +        addmoduleobject = _testlimitedcapi.PyImport_AddModuleObject | 
|  | 90 | +        self.check_addmodule(addmoduleobject, accept_nonstr=True) | 
|  | 91 | + | 
|  | 92 | +        self.assertRaises(TypeError, addmoduleobject, [])  # unhashable | 
|  | 93 | +        # CRASHES addmoduleobject(NULL) | 
|  | 94 | + | 
|  | 95 | +    def test_addmodule(self): | 
|  | 96 | +        # Test PyImport_AddModule() | 
|  | 97 | +        addmodule = _testlimitedcapi.PyImport_AddModule | 
|  | 98 | +        self.check_addmodule(addmodule) | 
|  | 99 | + | 
|  | 100 | +        self.assertRaises(UnicodeDecodeError, addmodule, b'\xff') | 
|  | 101 | +        # CRASHES addmodule(NULL) | 
|  | 102 | + | 
|  | 103 | +    def test_addmoduleref(self): | 
|  | 104 | +        # Test PyImport_AddModuleRef() | 
|  | 105 | +        addmoduleref = _testlimitedcapi.PyImport_AddModuleRef | 
|  | 106 | +        self.check_addmodule(addmoduleref) | 
|  | 107 | + | 
|  | 108 | +        self.assertRaises(UnicodeDecodeError, addmoduleref, b'\xff') | 
|  | 109 | +        # CRASHES addmoduleref(NULL) | 
|  | 110 | + | 
|  | 111 | +    def check_import_func(self, import_module): | 
|  | 112 | +        self.check_import_loaded_module(import_module) | 
|  | 113 | +        self.check_import_fresh_module(import_module) | 
|  | 114 | +        self.assertRaises(ModuleNotFoundError, import_module, 'nonexistent') | 
|  | 115 | +        self.assertRaises(ValueError, import_module, '') | 
|  | 116 | + | 
|  | 117 | +    def test_import(self): | 
|  | 118 | +        # Test PyImport_Import() | 
|  | 119 | +        import_ = _testlimitedcapi.PyImport_Import | 
|  | 120 | +        self.check_import_func(import_) | 
|  | 121 | + | 
|  | 122 | +        self.assertRaises(TypeError, import_, b'os') | 
|  | 123 | +        self.assertRaises(SystemError, import_, NULL) | 
|  | 124 | + | 
|  | 125 | +    def test_importmodule(self): | 
|  | 126 | +        # Test PyImport_ImportModule() | 
|  | 127 | +        importmodule = _testlimitedcapi.PyImport_ImportModule | 
|  | 128 | +        self.check_import_func(importmodule) | 
|  | 129 | + | 
|  | 130 | +        self.assertRaises(UnicodeDecodeError, importmodule, b'\xff') | 
|  | 131 | +        # CRASHES importmodule(NULL) | 
|  | 132 | + | 
|  | 133 | +    def test_importmodulenoblock(self): | 
|  | 134 | +        # Test deprecated PyImport_ImportModuleNoBlock() | 
|  | 135 | +        importmodulenoblock = _testlimitedcapi.PyImport_ImportModuleNoBlock | 
|  | 136 | +        with check_warnings(('', DeprecationWarning)): | 
|  | 137 | +            self.check_import_func(importmodulenoblock) | 
|  | 138 | +            self.assertRaises(UnicodeDecodeError, importmodulenoblock, b'\xff') | 
|  | 139 | + | 
|  | 140 | +        # CRASHES importmodulenoblock(NULL) | 
|  | 141 | + | 
|  | 142 | +    def check_frozen_import(self, import_frozen_module): | 
|  | 143 | +        # Importing a frozen module executes its code, so start by unloading | 
|  | 144 | +        # the module to execute the code in a new (temporary) module. | 
|  | 145 | +        old_zipimport = sys.modules.pop('zipimport') | 
|  | 146 | +        try: | 
|  | 147 | +            self.assertEqual(import_frozen_module('zipimport'), 1) | 
|  | 148 | + | 
|  | 149 | +            # import zipimport again | 
|  | 150 | +            self.assertEqual(import_frozen_module('zipimport'), 1) | 
|  | 151 | +        finally: | 
|  | 152 | +            sys.modules['zipimport'] = old_zipimport | 
|  | 153 | + | 
|  | 154 | +        # not a frozen module | 
|  | 155 | +        self.assertEqual(import_frozen_module('sys'), 0) | 
|  | 156 | +        self.assertEqual(import_frozen_module('nonexistent'), 0) | 
|  | 157 | +        self.assertEqual(import_frozen_module(''), 0) | 
|  | 158 | + | 
|  | 159 | +    def test_importfrozenmodule(self): | 
|  | 160 | +        # Test PyImport_ImportFrozenModule() | 
|  | 161 | +        importfrozenmodule = _testlimitedcapi.PyImport_ImportFrozenModule | 
|  | 162 | +        self.check_frozen_import(importfrozenmodule) | 
|  | 163 | + | 
|  | 164 | +        self.assertRaises(UnicodeDecodeError, importfrozenmodule, b'\xff') | 
|  | 165 | +        # CRASHES importfrozenmodule(NULL) | 
|  | 166 | + | 
|  | 167 | +    def test_importfrozenmoduleobject(self): | 
|  | 168 | +        # Test PyImport_ImportFrozenModuleObject() | 
|  | 169 | +        importfrozenmoduleobject = _testlimitedcapi.PyImport_ImportFrozenModuleObject | 
|  | 170 | +        self.check_frozen_import(importfrozenmoduleobject) | 
|  | 171 | +        self.assertEqual(importfrozenmoduleobject(b'zipimport'), 0) | 
|  | 172 | +        self.assertEqual(importfrozenmoduleobject(NULL), 0) | 
|  | 173 | + | 
|  | 174 | +    def test_importmoduleex(self): | 
|  | 175 | +        # Test PyImport_ImportModuleEx() | 
|  | 176 | +        importmoduleex = _testlimitedcapi.PyImport_ImportModuleEx | 
|  | 177 | +        self.check_import_func(lambda name: importmoduleex(name, NULL, NULL, NULL)) | 
|  | 178 | + | 
|  | 179 | +        self.assertRaises(ModuleNotFoundError, importmoduleex, 'nonexistent', NULL, NULL, NULL) | 
|  | 180 | +        self.assertRaises(ValueError, importmoduleex, '', NULL, NULL, NULL) | 
|  | 181 | +        self.assertRaises(UnicodeDecodeError, importmoduleex, b'\xff', NULL, NULL, NULL) | 
|  | 182 | +        # CRASHES importmoduleex(NULL, NULL, NULL, NULL) | 
|  | 183 | + | 
|  | 184 | +    def check_importmodulelevel(self, importmodulelevel): | 
|  | 185 | +        self.check_import_func(lambda name: importmodulelevel(name, NULL, NULL, NULL, 0)) | 
|  | 186 | + | 
|  | 187 | +        self.assertRaises(ModuleNotFoundError, importmodulelevel, 'nonexistent', NULL, NULL, NULL, 0) | 
|  | 188 | +        self.assertRaises(ValueError, importmodulelevel, '', NULL, NULL, NULL, 0) | 
|  | 189 | + | 
|  | 190 | +        if __package__: | 
|  | 191 | +            self.assertIs(importmodulelevel('test_import', globals(), NULL, NULL, 1), | 
|  | 192 | +                          sys.modules['test.test_capi.test_import']) | 
|  | 193 | +            self.assertIs(importmodulelevel('test_capi', globals(), NULL, NULL, 2), | 
|  | 194 | +                          sys.modules['test.test_capi']) | 
|  | 195 | +        self.assertRaises(ValueError, importmodulelevel, 'os', NULL, NULL, NULL, -1) | 
|  | 196 | +        with self.assertWarns(ImportWarning): | 
|  | 197 | +            self.assertRaises(KeyError, importmodulelevel, 'test_import', {}, NULL, NULL, 1) | 
|  | 198 | +        self.assertRaises(TypeError, importmodulelevel, 'test_import', [], NULL, NULL, 1) | 
|  | 199 | + | 
|  | 200 | +    def test_importmodulelevel(self): | 
|  | 201 | +        # Test PyImport_ImportModuleLevel() | 
|  | 202 | +        importmodulelevel = _testlimitedcapi.PyImport_ImportModuleLevel | 
|  | 203 | +        self.check_importmodulelevel(importmodulelevel) | 
|  | 204 | + | 
|  | 205 | +        self.assertRaises(UnicodeDecodeError, importmodulelevel, b'\xff', NULL, NULL, NULL, 0) | 
|  | 206 | +        # CRASHES importmodulelevel(NULL, NULL, NULL, NULL, 0) | 
|  | 207 | + | 
|  | 208 | +    def test_importmodulelevelobject(self): | 
|  | 209 | +        # Test PyImport_ImportModuleLevelObject() | 
|  | 210 | +        importmodulelevel = _testlimitedcapi.PyImport_ImportModuleLevelObject | 
|  | 211 | +        self.check_importmodulelevel(importmodulelevel) | 
|  | 212 | + | 
|  | 213 | +        self.assertRaises(TypeError, importmodulelevel, b'os', NULL, NULL, NULL, 0) | 
|  | 214 | +        self.assertRaises(ValueError, importmodulelevel, NULL, NULL, NULL, NULL, 0) | 
|  | 215 | + | 
|  | 216 | +    def check_executecodemodule(self, execute_code, *args): | 
|  | 217 | +        name = 'test_import_executecode' | 
|  | 218 | +        try: | 
|  | 219 | +            # Create a temporary module where the code will be executed | 
|  | 220 | +            self.assertNotIn(name, sys.modules) | 
|  | 221 | +            module = _testlimitedcapi.PyImport_AddModuleRef(name) | 
|  | 222 | +            self.assertFalse(hasattr(module, 'attr')) | 
|  | 223 | + | 
|  | 224 | +            # Execute the code | 
|  | 225 | +            code = compile('attr = 1', '<test>', 'exec') | 
|  | 226 | +            module2 = execute_code(name, code, *args) | 
|  | 227 | +            self.assertIs(module2, module) | 
|  | 228 | + | 
|  | 229 | +            # Check the function side effects | 
|  | 230 | +            self.assertEqual(module.attr, 1) | 
|  | 231 | +        finally: | 
|  | 232 | +            sys.modules.pop(name, None) | 
|  | 233 | +        return module.__spec__.origin | 
|  | 234 | + | 
|  | 235 | +    def test_executecodemodule(self): | 
|  | 236 | +        # Test PyImport_ExecCodeModule() | 
|  | 237 | +        execcodemodule = _testlimitedcapi.PyImport_ExecCodeModule | 
|  | 238 | +        self.check_executecodemodule(execcodemodule) | 
|  | 239 | + | 
|  | 240 | +        code = compile('attr = 1', '<test>', 'exec') | 
|  | 241 | +        self.assertRaises(UnicodeDecodeError, execcodemodule, b'\xff', code) | 
|  | 242 | +        # CRASHES execcodemodule(NULL, code) | 
|  | 243 | +        # CRASHES execcodemodule(name, NULL) | 
|  | 244 | + | 
|  | 245 | +    def test_executecodemoduleex(self): | 
|  | 246 | +        # Test PyImport_ExecCodeModuleEx() | 
|  | 247 | +        execcodemoduleex = _testlimitedcapi.PyImport_ExecCodeModuleEx | 
|  | 248 | + | 
|  | 249 | +        # Test NULL path (it should not crash) | 
|  | 250 | +        self.check_executecodemodule(execcodemoduleex, NULL) | 
|  | 251 | + | 
|  | 252 | +        # Test non-NULL path | 
|  | 253 | +        pathname = b'pathname' | 
|  | 254 | +        origin = self.check_executecodemodule(execcodemoduleex, pathname) | 
|  | 255 | +        self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) | 
|  | 256 | + | 
|  | 257 | +        pathname = os_helper.TESTFN_UNDECODABLE | 
|  | 258 | +        if pathname: | 
|  | 259 | +            origin = self.check_executecodemodule(execcodemoduleex, pathname) | 
|  | 260 | +            self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) | 
|  | 261 | + | 
|  | 262 | +        code = compile('attr = 1', '<test>', 'exec') | 
|  | 263 | +        self.assertRaises(UnicodeDecodeError, execcodemoduleex, b'\xff', code, NULL) | 
|  | 264 | +        # CRASHES execcodemoduleex(NULL, code, NULL) | 
|  | 265 | +        # CRASHES execcodemoduleex(name, NULL, NULL) | 
|  | 266 | + | 
|  | 267 | +    def check_executecode_pathnames(self, execute_code_func, object=False): | 
|  | 268 | +        # Test non-NULL pathname and NULL cpathname | 
|  | 269 | + | 
|  | 270 | +        # Test NULL paths (it should not crash) | 
|  | 271 | +        self.check_executecodemodule(execute_code_func, NULL, NULL) | 
|  | 272 | + | 
|  | 273 | +        pathname = 'pathname' | 
|  | 274 | +        origin = self.check_executecodemodule(execute_code_func, pathname, NULL) | 
|  | 275 | +        self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) | 
|  | 276 | +        origin = self.check_executecodemodule(execute_code_func, NULL, pathname) | 
|  | 277 | +        if not object: | 
|  | 278 | +            self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) | 
|  | 279 | + | 
|  | 280 | +        pathname = os_helper.TESTFN_UNDECODABLE | 
|  | 281 | +        if pathname: | 
|  | 282 | +            if object: | 
|  | 283 | +                pathname = os.fsdecode(pathname) | 
|  | 284 | +            origin = self.check_executecodemodule(execute_code_func, pathname, NULL) | 
|  | 285 | +            self.assertEqual(origin, os.path.abspath(os.fsdecode(pathname))) | 
|  | 286 | +            self.check_executecodemodule(execute_code_func, NULL, pathname) | 
|  | 287 | + | 
|  | 288 | +        # Test NULL pathname and non-NULL cpathname | 
|  | 289 | +        pyc_filename = importlib.util.cache_from_source(__file__) | 
|  | 290 | +        py_filename = importlib.util.source_from_cache(pyc_filename) | 
|  | 291 | +        origin = self.check_executecodemodule(execute_code_func, NULL, pyc_filename) | 
|  | 292 | +        if not object: | 
|  | 293 | +            self.assertEqual(origin, py_filename) | 
|  | 294 | + | 
|  | 295 | +    def test_executecodemodulewithpathnames(self): | 
|  | 296 | +        # Test PyImport_ExecCodeModuleWithPathnames() | 
|  | 297 | +        execute_code_func = _testlimitedcapi.PyImport_ExecCodeModuleWithPathnames | 
|  | 298 | +        self.check_executecode_pathnames(execute_code_func) | 
|  | 299 | + | 
|  | 300 | +        code = compile('attr = 1', '<test>', 'exec') | 
|  | 301 | +        self.assertRaises(UnicodeDecodeError, execute_code_func, b'\xff', code, NULL, NULL) | 
|  | 302 | +        # CRASHES execute_code_func(NULL, code, NULL, NULL) | 
|  | 303 | +        # CRASHES execute_code_func(name, NULL, NULL, NULL) | 
|  | 304 | + | 
|  | 305 | +    def test_executecodemoduleobject(self): | 
|  | 306 | +        # Test PyImport_ExecCodeModuleObject() | 
|  | 307 | +        execute_code_func = _testlimitedcapi.PyImport_ExecCodeModuleObject | 
|  | 308 | +        self.check_executecode_pathnames(execute_code_func, object=True) | 
|  | 309 | + | 
|  | 310 | +        code = compile('attr = 1', '<test>', 'exec') | 
|  | 311 | +        self.assertRaises(TypeError, execute_code_func, [], code, NULL, NULL) | 
|  | 312 | +        # CRASHES execute_code_func(NULL, code, NULL, NULL) | 
|  | 313 | +        # CRASHES execute_code_func(name, NULL, NULL, NULL) | 
|  | 314 | + | 
|  | 315 | +    # TODO: test PyImport_GetImporter() | 
|  | 316 | +    # TODO: test PyImport_ReloadModule() | 
|  | 317 | +    # TODO: test PyImport_ExtendInittab() | 
|  | 318 | +    # PyImport_AppendInittab() is tested by test_embed | 
|  | 319 | + | 
|  | 320 | + | 
|  | 321 | +if __name__ == "__main__": | 
|  | 322 | +    unittest.main() | 
0 commit comments