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