1import os 2import errno 3import importlib.machinery 4import py_compile 5import shutil 6import unittest 7import tempfile 8 9from test import support 10 11import modulefinder 12 13TEST_DIR = tempfile.mkdtemp() 14TEST_PATH = [TEST_DIR, os.path.dirname(tempfile.__file__)] 15 16# Each test description is a list of 5 items: 17# 18# 1. a module name that will be imported by modulefinder 19# 2. a list of module names that modulefinder is required to find 20# 3. a list of module names that modulefinder should complain 21# about because they are not found 22# 4. a list of module names that modulefinder should complain 23# about because they MAY be not found 24# 5. a string specifying packages to create; the format is obvious imo. 25# 26# Each package will be created in TEST_DIR, and TEST_DIR will be 27# removed after the tests again. 28# Modulefinder searches in a path that contains TEST_DIR, plus 29# the standard Lib directory. 30 31maybe_test = [ 32 "a.module", 33 ["a", "a.module", "sys", 34 "b"], 35 ["c"], ["b.something"], 36 """\ 37a/__init__.py 38a/module.py 39 from b import something 40 from c import something 41b/__init__.py 42 from sys import * 43"""] 44 45maybe_test_new = [ 46 "a.module", 47 ["a", "a.module", "sys", 48 "b", "__future__"], 49 ["c"], ["b.something"], 50 """\ 51a/__init__.py 52a/module.py 53 from b import something 54 from c import something 55b/__init__.py 56 from __future__ import absolute_import 57 from sys import * 58"""] 59 60package_test = [ 61 "a.module", 62 ["a", "a.b", "a.c", "a.module", "mymodule", "sys"], 63 ["blahblah", "c"], [], 64 """\ 65mymodule.py 66a/__init__.py 67 import blahblah 68 from a import b 69 import c 70a/module.py 71 import sys 72 from a import b as x 73 from a.c import sillyname 74a/b.py 75a/c.py 76 from a.module import x 77 import mymodule as sillyname 78 from sys import version_info 79"""] 80 81absolute_import_test = [ 82 "a.module", 83 ["a", "a.module", 84 "b", "b.x", "b.y", "b.z", 85 "__future__", "sys", "gc"], 86 ["blahblah", "z"], [], 87 """\ 88mymodule.py 89a/__init__.py 90a/module.py 91 from __future__ import absolute_import 92 import sys # sys 93 import blahblah # fails 94 import gc # gc 95 import b.x # b.x 96 from b import y # b.y 97 from b.z import * # b.z.* 98a/gc.py 99a/sys.py 100 import mymodule 101a/b/__init__.py 102a/b/x.py 103a/b/y.py 104a/b/z.py 105b/__init__.py 106 import z 107b/unused.py 108b/x.py 109b/y.py 110b/z.py 111"""] 112 113relative_import_test = [ 114 "a.module", 115 ["__future__", 116 "a", "a.module", 117 "a.b", "a.b.y", "a.b.z", 118 "a.b.c", "a.b.c.moduleC", 119 "a.b.c.d", "a.b.c.e", 120 "a.b.x", 121 "gc"], 122 [], [], 123 """\ 124mymodule.py 125a/__init__.py 126 from .b import y, z # a.b.y, a.b.z 127a/module.py 128 from __future__ import absolute_import # __future__ 129 import gc # gc 130a/gc.py 131a/sys.py 132a/b/__init__.py 133 from ..b import x # a.b.x 134 #from a.b.c import moduleC 135 from .c import moduleC # a.b.moduleC 136a/b/x.py 137a/b/y.py 138a/b/z.py 139a/b/g.py 140a/b/c/__init__.py 141 from ..c import e # a.b.c.e 142a/b/c/moduleC.py 143 from ..c import d # a.b.c.d 144a/b/c/d.py 145a/b/c/e.py 146a/b/c/x.py 147"""] 148 149relative_import_test_2 = [ 150 "a.module", 151 ["a", "a.module", 152 "a.sys", 153 "a.b", "a.b.y", "a.b.z", 154 "a.b.c", "a.b.c.d", 155 "a.b.c.e", 156 "a.b.c.moduleC", 157 "a.b.c.f", 158 "a.b.x", 159 "a.another"], 160 [], [], 161 """\ 162mymodule.py 163a/__init__.py 164 from . import sys # a.sys 165a/another.py 166a/module.py 167 from .b import y, z # a.b.y, a.b.z 168a/gc.py 169a/sys.py 170a/b/__init__.py 171 from .c import moduleC # a.b.c.moduleC 172 from .c import d # a.b.c.d 173a/b/x.py 174a/b/y.py 175a/b/z.py 176a/b/c/__init__.py 177 from . import e # a.b.c.e 178a/b/c/moduleC.py 179 # 180 from . import f # a.b.c.f 181 from .. import x # a.b.x 182 from ... import another # a.another 183a/b/c/d.py 184a/b/c/e.py 185a/b/c/f.py 186"""] 187 188relative_import_test_3 = [ 189 "a.module", 190 ["a", "a.module"], 191 ["a.bar"], 192 [], 193 """\ 194a/__init__.py 195 def foo(): pass 196a/module.py 197 from . import foo 198 from . import bar 199"""] 200 201relative_import_test_4 = [ 202 "a.module", 203 ["a", "a.module"], 204 [], 205 [], 206 """\ 207a/__init__.py 208 def foo(): pass 209a/module.py 210 from . import * 211"""] 212 213bytecode_test = [ 214 "a", 215 ["a"], 216 [], 217 [], 218 "" 219] 220 221 222def open_file(path): 223 dirname = os.path.dirname(path) 224 try: 225 os.makedirs(dirname) 226 except OSError as e: 227 if e.errno != errno.EEXIST: 228 raise 229 return open(path, "w") 230 231 232def create_package(source): 233 ofi = None 234 try: 235 for line in source.splitlines(): 236 if line.startswith(" ") or line.startswith("\t"): 237 ofi.write(line.strip() + "\n") 238 else: 239 if ofi: 240 ofi.close() 241 ofi = open_file(os.path.join(TEST_DIR, line.strip())) 242 finally: 243 if ofi: 244 ofi.close() 245 246 247class ModuleFinderTest(unittest.TestCase): 248 def _do_test(self, info, report=False, debug=0, replace_paths=[]): 249 import_this, modules, missing, maybe_missing, source = info 250 create_package(source) 251 try: 252 mf = modulefinder.ModuleFinder(path=TEST_PATH, debug=debug, 253 replace_paths=replace_paths) 254 mf.import_hook(import_this) 255 if report: 256 mf.report() 257## # This wouldn't work in general when executed several times: 258## opath = sys.path[:] 259## sys.path = TEST_PATH 260## try: 261## __import__(import_this) 262## except: 263## import traceback; traceback.print_exc() 264## sys.path = opath 265## return 266 modules = sorted(set(modules)) 267 found = sorted(mf.modules) 268 # check if we found what we expected, not more, not less 269 self.assertEqual(found, modules) 270 271 # check for missing and maybe missing modules 272 bad, maybe = mf.any_missing_maybe() 273 self.assertEqual(bad, missing) 274 self.assertEqual(maybe, maybe_missing) 275 finally: 276 shutil.rmtree(TEST_DIR) 277 278 def test_package(self): 279 self._do_test(package_test) 280 281 def test_maybe(self): 282 self._do_test(maybe_test) 283 284 def test_maybe_new(self): 285 self._do_test(maybe_test_new) 286 287 def test_absolute_imports(self): 288 self._do_test(absolute_import_test) 289 290 def test_relative_imports(self): 291 self._do_test(relative_import_test) 292 293 def test_relative_imports_2(self): 294 self._do_test(relative_import_test_2) 295 296 def test_relative_imports_3(self): 297 self._do_test(relative_import_test_3) 298 299 def test_relative_imports_4(self): 300 self._do_test(relative_import_test_4) 301 302 def test_bytecode(self): 303 base_path = os.path.join(TEST_DIR, 'a') 304 source_path = base_path + importlib.machinery.SOURCE_SUFFIXES[0] 305 bytecode_path = base_path + importlib.machinery.BYTECODE_SUFFIXES[0] 306 with open_file(source_path) as file: 307 file.write('testing_modulefinder = True\n') 308 py_compile.compile(source_path, cfile=bytecode_path) 309 os.remove(source_path) 310 self._do_test(bytecode_test) 311 312 def test_replace_paths(self): 313 old_path = os.path.join(TEST_DIR, 'a', 'module.py') 314 new_path = os.path.join(TEST_DIR, 'a', 'spam.py') 315 with support.captured_stdout() as output: 316 self._do_test(maybe_test, debug=2, 317 replace_paths=[(old_path, new_path)]) 318 output = output.getvalue() 319 expected = "co_filename %r changed to %r" % (old_path, new_path) 320 self.assertIn(expected, output) 321 322 def test_extended_opargs(self): 323 extended_opargs_test = [ 324 "a", 325 ["a", "b"], 326 [], [], 327 """\ 328a.py 329 %r 330 import b 331b.py 332""" % list(range(2**16))] # 2**16 constants 333 self._do_test(extended_opargs_test) 334 335 336if __name__ == "__main__": 337 unittest.main() 338