Home | History | Annotate | Download | only in test
      1 import os
      2 import errno
      3 import importlib.machinery
      4 import py_compile
      5 import shutil
      6 import unittest
      7 import tempfile
      8 
      9 from test import support
     10 
     11 import modulefinder
     12 
     13 TEST_DIR = tempfile.mkdtemp()
     14 TEST_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 
     31 maybe_test = [
     32     "a.module",
     33     ["a", "a.module", "sys",
     34      "b"],
     35     ["c"], ["b.something"],
     36     """\
     37 a/__init__.py
     38 a/module.py
     39                                 from b import something
     40                                 from c import something
     41 b/__init__.py
     42                                 from sys import *
     43 """]
     44 
     45 maybe_test_new = [
     46     "a.module",
     47     ["a", "a.module", "sys",
     48      "b", "__future__"],
     49     ["c"], ["b.something"],
     50     """\
     51 a/__init__.py
     52 a/module.py
     53                                 from b import something
     54                                 from c import something
     55 b/__init__.py
     56                                 from __future__ import absolute_import
     57                                 from sys import *
     58 """]
     59 
     60 package_test = [
     61     "a.module",
     62     ["a", "a.b", "a.c", "a.module", "mymodule", "sys"],
     63     ["blahblah", "c"], [],
     64     """\
     65 mymodule.py
     66 a/__init__.py
     67                                 import blahblah
     68                                 from a import b
     69                                 import c
     70 a/module.py
     71                                 import sys
     72                                 from a import b as x
     73                                 from a.c import sillyname
     74 a/b.py
     75 a/c.py
     76                                 from a.module import x
     77                                 import mymodule as sillyname
     78                                 from sys import version_info
     79 """]
     80 
     81 absolute_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     """\
     88 mymodule.py
     89 a/__init__.py
     90 a/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.*
     98 a/gc.py
     99 a/sys.py
    100                                 import mymodule
    101 a/b/__init__.py
    102 a/b/x.py
    103 a/b/y.py
    104 a/b/z.py
    105 b/__init__.py
    106                                 import z
    107 b/unused.py
    108 b/x.py
    109 b/y.py
    110 b/z.py
    111 """]
    112 
    113 relative_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     """\
    124 mymodule.py
    125 a/__init__.py
    126                                 from .b import y, z # a.b.y, a.b.z
    127 a/module.py
    128                                 from __future__ import absolute_import # __future__
    129                                 import gc # gc
    130 a/gc.py
    131 a/sys.py
    132 a/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
    136 a/b/x.py
    137 a/b/y.py
    138 a/b/z.py
    139 a/b/g.py
    140 a/b/c/__init__.py
    141                                 from ..c import e # a.b.c.e
    142 a/b/c/moduleC.py
    143                                 from ..c import d # a.b.c.d
    144 a/b/c/d.py
    145 a/b/c/e.py
    146 a/b/c/x.py
    147 """]
    148 
    149 relative_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     """\
    162 mymodule.py
    163 a/__init__.py
    164                                 from . import sys # a.sys
    165 a/another.py
    166 a/module.py
    167                                 from .b import y, z # a.b.y, a.b.z
    168 a/gc.py
    169 a/sys.py
    170 a/b/__init__.py
    171                                 from .c import moduleC # a.b.c.moduleC
    172                                 from .c import d # a.b.c.d
    173 a/b/x.py
    174 a/b/y.py
    175 a/b/z.py
    176 a/b/c/__init__.py
    177                                 from . import e # a.b.c.e
    178 a/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
    183 a/b/c/d.py
    184 a/b/c/e.py
    185 a/b/c/f.py
    186 """]
    187 
    188 relative_import_test_3 = [
    189     "a.module",
    190     ["a", "a.module"],
    191     ["a.bar"],
    192     [],
    193     """\
    194 a/__init__.py
    195                                 def foo(): pass
    196 a/module.py
    197                                 from . import foo
    198                                 from . import bar
    199 """]
    200 
    201 relative_import_test_4 = [
    202     "a.module",
    203     ["a", "a.module"],
    204     [],
    205     [],
    206     """\
    207 a/__init__.py
    208                                 def foo(): pass
    209 a/module.py
    210                                 from . import *
    211 """]
    212 
    213 bytecode_test = [
    214     "a",
    215     ["a"],
    216     [],
    217     [],
    218     ""
    219 ]
    220 
    221 
    222 def 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 
    232 def 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 
    247 class 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             """\
    328 a.py
    329                                 %r
    330                                 import b
    331 b.py
    332 """ % list(range(2**16))]  # 2**16 constants
    333         self._do_test(extended_opargs_test)
    334 
    335 
    336 if __name__ == "__main__":
    337     unittest.main()
    338