1import unittest 2from test import support 3from test.support import warnings_helper 4import os 5import sys 6 7 8class NoAll(RuntimeError): 9 pass 10 11class FailedImport(RuntimeError): 12 pass 13 14 15class AllTest(unittest.TestCase): 16 17 def check_all(self, modname): 18 names = {} 19 with warnings_helper.check_warnings( 20 (".* (module|package)", DeprecationWarning), 21 (".* (module|package)", PendingDeprecationWarning), 22 ("", ResourceWarning), 23 quiet=True): 24 try: 25 exec("import %s" % modname, names) 26 except: 27 # Silent fail here seems the best route since some modules 28 # may not be available or not initialize properly in all 29 # environments. 30 raise FailedImport(modname) 31 if not hasattr(sys.modules[modname], "__all__"): 32 raise NoAll(modname) 33 names = {} 34 with self.subTest(module=modname): 35 with warnings_helper.check_warnings( 36 ("", DeprecationWarning), 37 ("", ResourceWarning), 38 quiet=True): 39 try: 40 exec("from %s import *" % modname, names) 41 except Exception as e: 42 # Include the module name in the exception string 43 self.fail("__all__ failure in {}: {}: {}".format( 44 modname, e.__class__.__name__, e)) 45 if "__builtins__" in names: 46 del names["__builtins__"] 47 if '__annotations__' in names: 48 del names['__annotations__'] 49 if "__warningregistry__" in names: 50 del names["__warningregistry__"] 51 keys = set(names) 52 all_list = sys.modules[modname].__all__ 53 all_set = set(all_list) 54 self.assertCountEqual(all_set, all_list, "in module {}".format(modname)) 55 self.assertEqual(keys, all_set, "in module {}".format(modname)) 56 57 def walk_modules(self, basedir, modpath): 58 for fn in sorted(os.listdir(basedir)): 59 path = os.path.join(basedir, fn) 60 if os.path.isdir(path): 61 pkg_init = os.path.join(path, '__init__.py') 62 if os.path.exists(pkg_init): 63 yield pkg_init, modpath + fn 64 for p, m in self.walk_modules(path, modpath + fn + "."): 65 yield p, m 66 continue 67 if not fn.endswith('.py') or fn == '__init__.py': 68 continue 69 yield path, modpath + fn[:-3] 70 71 def test_all(self): 72 # List of denied modules and packages 73 denylist = set([ 74 # Will raise a SyntaxError when compiling the exec statement 75 '__future__', 76 ]) 77 78 if not sys.platform.startswith('java'): 79 # In case _socket fails to build, make this test fail more gracefully 80 # than an AttributeError somewhere deep in CGIHTTPServer. 81 import _socket 82 83 ignored = [] 84 failed_imports = [] 85 lib_dir = os.path.dirname(os.path.dirname(__file__)) 86 for path, modname in self.walk_modules(lib_dir, ""): 87 m = modname 88 denied = False 89 while m: 90 if m in denylist: 91 denied = True 92 break 93 m = m.rpartition('.')[0] 94 if denied: 95 continue 96 if support.verbose: 97 print(modname) 98 try: 99 # This heuristic speeds up the process by removing, de facto, 100 # most test modules (and avoiding the auto-executing ones). 101 with open(path, "rb") as f: 102 if b"__all__" not in f.read(): 103 raise NoAll(modname) 104 self.check_all(modname) 105 except NoAll: 106 ignored.append(modname) 107 except FailedImport: 108 failed_imports.append(modname) 109 110 if support.verbose: 111 print('Following modules have no __all__ and have been ignored:', 112 ignored) 113 print('Following modules failed to be imported:', failed_imports) 114 115 116if __name__ == "__main__": 117 unittest.main() 118