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