Home | History | Annotate | Download | only in test
      1 from test.support import run_unittest, unload, check_warnings, CleanImport
      2 import unittest
      3 import sys
      4 import importlib
      5 from importlib.util import spec_from_file_location
      6 import pkgutil
      7 import os
      8 import os.path
      9 import tempfile
     10 import shutil
     11 import zipfile
     12 
     13 # Note: pkgutil.walk_packages is currently tested in test_runpy. This is
     14 # a hack to get a major issue resolved for 3.3b2. Longer term, it should
     15 # be moved back here, perhaps by factoring out the helper code for
     16 # creating interesting package layouts to a separate module.
     17 # Issue #15348 declares this is indeed a dodgy hack ;)
     18 
     19 class PkgutilTests(unittest.TestCase):
     20 
     21     def setUp(self):
     22         self.dirname = tempfile.mkdtemp()
     23         self.addCleanup(shutil.rmtree, self.dirname)
     24         sys.path.insert(0, self.dirname)
     25 
     26     def tearDown(self):
     27         del sys.path[0]
     28 
     29     def test_getdata_filesys(self):
     30         pkg = 'test_getdata_filesys'
     31 
     32         # Include a LF and a CRLF, to test that binary data is read back
     33         RESOURCE_DATA = b'Hello, world!\nSecond line\r\nThird line'
     34 
     35         # Make a package with some resources
     36         package_dir = os.path.join(self.dirname, pkg)
     37         os.mkdir(package_dir)
     38         # Empty init.py
     39         f = open(os.path.join(package_dir, '__init__.py'), "wb")
     40         f.close()
     41         # Resource files, res.txt, sub/res.txt
     42         f = open(os.path.join(package_dir, 'res.txt'), "wb")
     43         f.write(RESOURCE_DATA)
     44         f.close()
     45         os.mkdir(os.path.join(package_dir, 'sub'))
     46         f = open(os.path.join(package_dir, 'sub', 'res.txt'), "wb")
     47         f.write(RESOURCE_DATA)
     48         f.close()
     49 
     50         # Check we can read the resources
     51         res1 = pkgutil.get_data(pkg, 'res.txt')
     52         self.assertEqual(res1, RESOURCE_DATA)
     53         res2 = pkgutil.get_data(pkg, 'sub/res.txt')
     54         self.assertEqual(res2, RESOURCE_DATA)
     55 
     56         del sys.modules[pkg]
     57 
     58     def test_getdata_zipfile(self):
     59         zip = 'test_getdata_zipfile.zip'
     60         pkg = 'test_getdata_zipfile'
     61 
     62         # Include a LF and a CRLF, to test that binary data is read back
     63         RESOURCE_DATA = b'Hello, world!\nSecond line\r\nThird line'
     64 
     65         # Make a package with some resources
     66         zip_file = os.path.join(self.dirname, zip)
     67         z = zipfile.ZipFile(zip_file, 'w')
     68 
     69         # Empty init.py
     70         z.writestr(pkg + '/__init__.py', "")
     71         # Resource files, res.txt, sub/res.txt
     72         z.writestr(pkg + '/res.txt', RESOURCE_DATA)
     73         z.writestr(pkg + '/sub/res.txt', RESOURCE_DATA)
     74         z.close()
     75 
     76         # Check we can read the resources
     77         sys.path.insert(0, zip_file)
     78         res1 = pkgutil.get_data(pkg, 'res.txt')
     79         self.assertEqual(res1, RESOURCE_DATA)
     80         res2 = pkgutil.get_data(pkg, 'sub/res.txt')
     81         self.assertEqual(res2, RESOURCE_DATA)
     82 
     83         names = []
     84         for moduleinfo in pkgutil.iter_modules([zip_file]):
     85             self.assertIsInstance(moduleinfo, pkgutil.ModuleInfo)
     86             names.append(moduleinfo.name)
     87         self.assertEqual(names, ['test_getdata_zipfile'])
     88 
     89         del sys.path[0]
     90 
     91         del sys.modules[pkg]
     92 
     93     def test_unreadable_dir_on_syspath(self):
     94         # issue7367 - walk_packages failed if unreadable dir on sys.path
     95         package_name = "unreadable_package"
     96         d = os.path.join(self.dirname, package_name)
     97         # this does not appear to create an unreadable dir on Windows
     98         #   but the test should not fail anyway
     99         os.mkdir(d, 0)
    100         self.addCleanup(os.rmdir, d)
    101         for t in pkgutil.walk_packages(path=[self.dirname]):
    102             self.fail("unexpected package found")
    103 
    104     def test_walkpackages_filesys(self):
    105         pkg1 = 'test_walkpackages_filesys'
    106         pkg1_dir = os.path.join(self.dirname, pkg1)
    107         os.mkdir(pkg1_dir)
    108         f = open(os.path.join(pkg1_dir, '__init__.py'), "wb")
    109         f.close()
    110         os.mkdir(os.path.join(pkg1_dir, 'sub'))
    111         f = open(os.path.join(pkg1_dir, 'sub', '__init__.py'), "wb")
    112         f.close()
    113         f = open(os.path.join(pkg1_dir, 'sub', 'mod.py'), "wb")
    114         f.close()
    115 
    116         # Now, to juice it up, let's add the opposite packages, too.
    117         pkg2 = 'sub'
    118         pkg2_dir = os.path.join(self.dirname, pkg2)
    119         os.mkdir(pkg2_dir)
    120         f = open(os.path.join(pkg2_dir, '__init__.py'), "wb")
    121         f.close()
    122         os.mkdir(os.path.join(pkg2_dir, 'test_walkpackages_filesys'))
    123         f = open(os.path.join(pkg2_dir, 'test_walkpackages_filesys', '__init__.py'), "wb")
    124         f.close()
    125         f = open(os.path.join(pkg2_dir, 'test_walkpackages_filesys', 'mod.py'), "wb")
    126         f.close()
    127 
    128         expected = [
    129             'sub',
    130             'sub.test_walkpackages_filesys',
    131             'sub.test_walkpackages_filesys.mod',
    132             'test_walkpackages_filesys',
    133             'test_walkpackages_filesys.sub',
    134             'test_walkpackages_filesys.sub.mod',
    135         ]
    136         actual= [e[1] for e in pkgutil.walk_packages([self.dirname])]
    137         self.assertEqual(actual, expected)
    138 
    139         for pkg in expected:
    140             if pkg.endswith('mod'):
    141                 continue
    142             del sys.modules[pkg]
    143 
    144     def test_walkpackages_zipfile(self):
    145         """Tests the same as test_walkpackages_filesys, only with a zip file."""
    146 
    147         zip = 'test_walkpackages_zipfile.zip'
    148         pkg1 = 'test_walkpackages_zipfile'
    149         pkg2 = 'sub'
    150 
    151         zip_file = os.path.join(self.dirname, zip)
    152         z = zipfile.ZipFile(zip_file, 'w')
    153         z.writestr(pkg2 + '/__init__.py', "")
    154         z.writestr(pkg2 + '/' + pkg1 + '/__init__.py', "")
    155         z.writestr(pkg2 + '/' + pkg1 + '/mod.py', "")
    156         z.writestr(pkg1 + '/__init__.py', "")
    157         z.writestr(pkg1 + '/' + pkg2 + '/__init__.py', "")
    158         z.writestr(pkg1 + '/' + pkg2 + '/mod.py', "")
    159         z.close()
    160 
    161         sys.path.insert(0, zip_file)
    162         expected = [
    163             'sub',
    164             'sub.test_walkpackages_zipfile',
    165             'sub.test_walkpackages_zipfile.mod',
    166             'test_walkpackages_zipfile',
    167             'test_walkpackages_zipfile.sub',
    168             'test_walkpackages_zipfile.sub.mod',
    169         ]
    170         actual= [e[1] for e in pkgutil.walk_packages([zip_file])]
    171         self.assertEqual(actual, expected)
    172         del sys.path[0]
    173 
    174         for pkg in expected:
    175             if pkg.endswith('mod'):
    176                 continue
    177             del sys.modules[pkg]
    178 
    179 
    180 
    181 class PkgutilPEP302Tests(unittest.TestCase):
    182 
    183     class MyTestLoader(object):
    184         def create_module(self, spec):
    185             return None
    186 
    187         def exec_module(self, mod):
    188             # Count how many times the module is reloaded
    189             mod.__dict__['loads'] = mod.__dict__.get('loads', 0) + 1
    190 
    191         def get_data(self, path):
    192             return "Hello, world!"
    193 
    194     class MyTestImporter(object):
    195         def find_spec(self, fullname, path=None, target=None):
    196             loader = PkgutilPEP302Tests.MyTestLoader()
    197             return spec_from_file_location(fullname,
    198                                            '<%s>' % loader.__class__.__name__,
    199                                            loader=loader,
    200                                            submodule_search_locations=[])
    201 
    202     def setUp(self):
    203         sys.meta_path.insert(0, self.MyTestImporter())
    204 
    205     def tearDown(self):
    206         del sys.meta_path[0]
    207 
    208     def test_getdata_pep302(self):
    209         # Use a dummy finder/loader
    210         self.assertEqual(pkgutil.get_data('foo', 'dummy'), "Hello, world!")
    211         del sys.modules['foo']
    212 
    213     def test_alreadyloaded(self):
    214         # Ensure that get_data works without reloading - the "loads" module
    215         # variable in the example loader should count how many times a reload
    216         # occurs.
    217         import foo
    218         self.assertEqual(foo.loads, 1)
    219         self.assertEqual(pkgutil.get_data('foo', 'dummy'), "Hello, world!")
    220         self.assertEqual(foo.loads, 1)
    221         del sys.modules['foo']
    222 
    223 
    224 # These tests, especially the setup and cleanup, are hideous. They
    225 # need to be cleaned up once issue 14715 is addressed.
    226 class ExtendPathTests(unittest.TestCase):
    227     def create_init(self, pkgname):
    228         dirname = tempfile.mkdtemp()
    229         sys.path.insert(0, dirname)
    230 
    231         pkgdir = os.path.join(dirname, pkgname)
    232         os.mkdir(pkgdir)
    233         with open(os.path.join(pkgdir, '__init__.py'), 'w') as fl:
    234             fl.write('from pkgutil import extend_path\n__path__ = extend_path(__path__, __name__)\n')
    235 
    236         return dirname
    237 
    238     def create_submodule(self, dirname, pkgname, submodule_name, value):
    239         module_name = os.path.join(dirname, pkgname, submodule_name + '.py')
    240         with open(module_name, 'w') as fl:
    241             print('value={}'.format(value), file=fl)
    242 
    243     def test_simple(self):
    244         pkgname = 'foo'
    245         dirname_0 = self.create_init(pkgname)
    246         dirname_1 = self.create_init(pkgname)
    247         self.create_submodule(dirname_0, pkgname, 'bar', 0)
    248         self.create_submodule(dirname_1, pkgname, 'baz', 1)
    249         import foo.bar
    250         import foo.baz
    251         # Ensure we read the expected values
    252         self.assertEqual(foo.bar.value, 0)
    253         self.assertEqual(foo.baz.value, 1)
    254 
    255         # Ensure the path is set up correctly
    256         self.assertEqual(sorted(foo.__path__),
    257                          sorted([os.path.join(dirname_0, pkgname),
    258                                  os.path.join(dirname_1, pkgname)]))
    259 
    260         # Cleanup
    261         shutil.rmtree(dirname_0)
    262         shutil.rmtree(dirname_1)
    263         del sys.path[0]
    264         del sys.path[0]
    265         del sys.modules['foo']
    266         del sys.modules['foo.bar']
    267         del sys.modules['foo.baz']
    268 
    269 
    270     # Another awful testing hack to be cleaned up once the test_runpy
    271     # helpers are factored out to a common location
    272     def test_iter_importers(self):
    273         iter_importers = pkgutil.iter_importers
    274         get_importer = pkgutil.get_importer
    275 
    276         pkgname = 'spam'
    277         modname = 'eggs'
    278         dirname = self.create_init(pkgname)
    279         pathitem = os.path.join(dirname, pkgname)
    280         fullname = '{}.{}'.format(pkgname, modname)
    281         sys.modules.pop(fullname, None)
    282         sys.modules.pop(pkgname, None)
    283         try:
    284             self.create_submodule(dirname, pkgname, modname, 0)
    285 
    286             importlib.import_module(fullname)
    287 
    288             importers = list(iter_importers(fullname))
    289             expected_importer = get_importer(pathitem)
    290             for finder in importers:
    291                 spec = pkgutil._get_spec(finder, fullname)
    292                 loader = spec.loader
    293                 try:
    294                     loader = loader.loader
    295                 except AttributeError:
    296                     # For now we still allow raw loaders from
    297                     # find_module().
    298                     pass
    299                 self.assertIsInstance(finder, importlib.machinery.FileFinder)
    300                 self.assertEqual(finder, expected_importer)
    301                 self.assertIsInstance(loader,
    302                                       importlib.machinery.SourceFileLoader)
    303                 self.assertIsNone(pkgutil._get_spec(finder, pkgname))
    304 
    305             with self.assertRaises(ImportError):
    306                 list(iter_importers('invalid.module'))
    307 
    308             with self.assertRaises(ImportError):
    309                 list(iter_importers('.spam'))
    310         finally:
    311             shutil.rmtree(dirname)
    312             del sys.path[0]
    313             try:
    314                 del sys.modules['spam']
    315                 del sys.modules['spam.eggs']
    316             except KeyError:
    317                 pass
    318 
    319 
    320     def test_mixed_namespace(self):
    321         pkgname = 'foo'
    322         dirname_0 = self.create_init(pkgname)
    323         dirname_1 = self.create_init(pkgname)
    324         self.create_submodule(dirname_0, pkgname, 'bar', 0)
    325         # Turn this into a PEP 420 namespace package
    326         os.unlink(os.path.join(dirname_0, pkgname, '__init__.py'))
    327         self.create_submodule(dirname_1, pkgname, 'baz', 1)
    328         import foo.bar
    329         import foo.baz
    330         # Ensure we read the expected values
    331         self.assertEqual(foo.bar.value, 0)
    332         self.assertEqual(foo.baz.value, 1)
    333 
    334         # Ensure the path is set up correctly
    335         self.assertEqual(sorted(foo.__path__),
    336                          sorted([os.path.join(dirname_0, pkgname),
    337                                  os.path.join(dirname_1, pkgname)]))
    338 
    339         # Cleanup
    340         shutil.rmtree(dirname_0)
    341         shutil.rmtree(dirname_1)
    342         del sys.path[0]
    343         del sys.path[0]
    344         del sys.modules['foo']
    345         del sys.modules['foo.bar']
    346         del sys.modules['foo.baz']
    347 
    348     # XXX: test .pkg files
    349 
    350 
    351 class NestedNamespacePackageTest(unittest.TestCase):
    352 
    353     def setUp(self):
    354         self.basedir = tempfile.mkdtemp()
    355         self.old_path = sys.path[:]
    356 
    357     def tearDown(self):
    358         sys.path[:] = self.old_path
    359         shutil.rmtree(self.basedir)
    360 
    361     def create_module(self, name, contents):
    362         base, final = name.rsplit('.', 1)
    363         base_path = os.path.join(self.basedir, base.replace('.', os.path.sep))
    364         os.makedirs(base_path, exist_ok=True)
    365         with open(os.path.join(base_path, final + ".py"), 'w') as f:
    366             f.write(contents)
    367 
    368     def test_nested(self):
    369         pkgutil_boilerplate = (
    370             'import pkgutil; '
    371             '__path__ = pkgutil.extend_path(__path__, __name__)')
    372         self.create_module('a.pkg.__init__', pkgutil_boilerplate)
    373         self.create_module('b.pkg.__init__', pkgutil_boilerplate)
    374         self.create_module('a.pkg.subpkg.__init__', pkgutil_boilerplate)
    375         self.create_module('b.pkg.subpkg.__init__', pkgutil_boilerplate)
    376         self.create_module('a.pkg.subpkg.c', 'c = 1')
    377         self.create_module('b.pkg.subpkg.d', 'd = 2')
    378         sys.path.insert(0, os.path.join(self.basedir, 'a'))
    379         sys.path.insert(0, os.path.join(self.basedir, 'b'))
    380         import pkg
    381         self.addCleanup(unload, 'pkg')
    382         self.assertEqual(len(pkg.__path__), 2)
    383         import pkg.subpkg
    384         self.addCleanup(unload, 'pkg.subpkg')
    385         self.assertEqual(len(pkg.subpkg.__path__), 2)
    386         from pkg.subpkg.c import c
    387         from pkg.subpkg.d import d
    388         self.assertEqual(c, 1)
    389         self.assertEqual(d, 2)
    390 
    391 
    392 class ImportlibMigrationTests(unittest.TestCase):
    393     # With full PEP 302 support in the standard import machinery, the
    394     # PEP 302 emulation in this module is in the process of being
    395     # deprecated in favour of importlib proper
    396 
    397     def check_deprecated(self):
    398         return check_warnings(
    399             ("This emulation is deprecated, use 'importlib' instead",
    400              DeprecationWarning))
    401 
    402     def test_importer_deprecated(self):
    403         with self.check_deprecated():
    404             pkgutil.ImpImporter("")
    405 
    406     def test_loader_deprecated(self):
    407         with self.check_deprecated():
    408             pkgutil.ImpLoader("", "", "", "")
    409 
    410     def test_get_loader_avoids_emulation(self):
    411         with check_warnings() as w:
    412             self.assertIsNotNone(pkgutil.get_loader("sys"))
    413             self.assertIsNotNone(pkgutil.get_loader("os"))
    414             self.assertIsNotNone(pkgutil.get_loader("test.support"))
    415             self.assertEqual(len(w.warnings), 0)
    416 
    417     @unittest.skipIf(__name__ == '__main__', 'not compatible with __main__')
    418     def test_get_loader_handles_missing_loader_attribute(self):
    419         global __loader__
    420         this_loader = __loader__
    421         del __loader__
    422         try:
    423             with check_warnings() as w:
    424                 self.assertIsNotNone(pkgutil.get_loader(__name__))
    425                 self.assertEqual(len(w.warnings), 0)
    426         finally:
    427             __loader__ = this_loader
    428 
    429     def test_get_loader_handles_missing_spec_attribute(self):
    430         name = 'spam'
    431         mod = type(sys)(name)
    432         del mod.__spec__
    433         with CleanImport(name):
    434             sys.modules[name] = mod
    435             loader = pkgutil.get_loader(name)
    436         self.assertIsNone(loader)
    437 
    438     def test_get_loader_handles_spec_attribute_none(self):
    439         name = 'spam'
    440         mod = type(sys)(name)
    441         mod.__spec__ = None
    442         with CleanImport(name):
    443             sys.modules[name] = mod
    444             loader = pkgutil.get_loader(name)
    445         self.assertIsNone(loader)
    446 
    447     def test_get_loader_None_in_sys_modules(self):
    448         name = 'totally bogus'
    449         sys.modules[name] = None
    450         try:
    451             loader = pkgutil.get_loader(name)
    452         finally:
    453             del sys.modules[name]
    454         self.assertIsNone(loader)
    455 
    456     def test_find_loader_missing_module(self):
    457         name = 'totally bogus'
    458         loader = pkgutil.find_loader(name)
    459         self.assertIsNone(loader)
    460 
    461     def test_find_loader_avoids_emulation(self):
    462         with check_warnings() as w:
    463             self.assertIsNotNone(pkgutil.find_loader("sys"))
    464             self.assertIsNotNone(pkgutil.find_loader("os"))
    465             self.assertIsNotNone(pkgutil.find_loader("test.support"))
    466             self.assertEqual(len(w.warnings), 0)
    467 
    468     def test_get_importer_avoids_emulation(self):
    469         # We use an illegal path so *none* of the path hooks should fire
    470         with check_warnings() as w:
    471             self.assertIsNone(pkgutil.get_importer("*??"))
    472             self.assertEqual(len(w.warnings), 0)
    473 
    474     def test_iter_importers_avoids_emulation(self):
    475         with check_warnings() as w:
    476             for importer in pkgutil.iter_importers(): pass
    477             self.assertEqual(len(w.warnings), 0)
    478 
    479 
    480 def test_main():
    481     run_unittest(PkgutilTests, PkgutilPEP302Tests, ExtendPathTests,
    482                  NestedNamespacePackageTest, ImportlibMigrationTests)
    483     # this is necessary if test is run repeated (like when finding leaks)
    484     import zipimport
    485     import importlib
    486     zipimport._zip_directory_cache.clear()
    487     importlib.invalidate_caches()
    488 
    489 
    490 if __name__ == '__main__':
    491     test_main()
    492