test___all__.py 5.1 KB

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