From 4de39cda18c1a0a0c5ca4ae31b272374f48d7480 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 10 Oct 2009 21:08:31 +0000 Subject: [PATCH] Merged revisions 75312 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75312 | antoine.pitrou | 2009-10-10 22:52:11 +0200 (sam., 10 oct. 2009) | 4 lines Issue #7055: test___all__ now greedily detects all modules which have an __all__ attribute, rather than using a hardcoded and incomplete list. ........ --- Lib/dbm/__init__.py | 2 +- Lib/test/test___all__.py | 206 +++++++++++++++------------------------ Misc/NEWS | 3 + 3 files changed, 83 insertions(+), 128 deletions(-) diff --git a/Lib/dbm/__init__.py b/Lib/dbm/__init__.py index 56555be78f3..c224847597f 100644 --- a/Lib/dbm/__init__.py +++ b/Lib/dbm/__init__.py @@ -36,7 +36,7 @@ Note: 'r' and 'w' fail if the database doesn't exist; 'c' creates it only if it doesn't exist; and 'n' always creates a new database. """ -__all__ = ['open', 'whichdb', 'error', 'errors'] +__all__ = ['open', 'whichdb', 'error', 'error'] import io import os diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index a4e69b20645..200db5cfa23 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -1,9 +1,17 @@ import unittest -from test.support import run_unittest +from test import support +import os import sys import warnings +class NoAll(RuntimeError): + pass + +class FailedImport(RuntimeError): + pass + + class AllTest(unittest.TestCase): def check_all(self, modname): @@ -13,153 +21,97 @@ class AllTest(unittest.TestCase): DeprecationWarning) try: exec("import %s" % modname, names) - except ImportError: + except: # Silent fail here seems the best route since some modules - # may not be available in all environments. - return - self.assertTrue(hasattr(sys.modules[modname], "__all__"), - "%s has no __all__ attribute" % modname) + # may not be available or not initialize properly in all + # environments. + raise FailedImport(modname) + if not hasattr(sys.modules[modname], "__all__"): + raise NoAll(modname) names = {} - exec("from %s import *" % modname, names) + try: + exec("from %s import *" % modname, names) + except Exception as e: + # Include the module name in the exception string + self.fail("__all__ failure in {}: {}: {}".format( + modname, e.__class__.__name__, e)) if "__builtins__" in names: del names["__builtins__"] keys = set(names) all = set(sys.modules[modname].__all__) self.assertEqual(keys, all) + def walk_modules(self, basedir, modpath): + for fn in sorted(os.listdir(basedir)): + path = os.path.join(basedir, fn) + if os.path.isdir(path): + pkg_init = os.path.join(path, '__init__.py') + if os.path.exists(pkg_init): + yield pkg_init, modpath + fn + for p, m in self.walk_modules(path, modpath + fn + "."): + yield p, m + continue + if not fn.endswith('.py') or fn == '__init__.py': + continue + yield path, modpath + fn[:-3] + def test_all(self): + # Blacklisted modules and packages + blacklist = set([ + # Will raise a SyntaxError when compiling the exec statement + '__future__', + ]) + if not sys.platform.startswith('java'): # In case _socket fails to build, make this test fail more gracefully # than an AttributeError somewhere deep in CGIHTTPServer. import _socket - self.check_all("http.server") - self.check_all("configparser") - self.check_all("http.cookies") - self.check_all("queue") - self.check_all("socketserver") - self.check_all("aifc") - self.check_all("base64") - self.check_all("bdb") - self.check_all("binhex") - self.check_all("calendar") - self.check_all("collections") - self.check_all("cgi") - self.check_all("cmd") - self.check_all("code") - self.check_all("codecs") - self.check_all("codeop") - self.check_all("colorsys") - self.check_all("compileall") - self.check_all("copy") - self.check_all("copyreg") - self.check_all("csv") - self.check_all("dbm.bsd") - self.check_all("decimal") - self.check_all("difflib") - self.check_all("dircache") - self.check_all("dis") - self.check_all("doctest") - self.check_all("_dummy_thread") - self.check_all("dummy_threading") - self.check_all("filecmp") - self.check_all("fileinput") - self.check_all("fnmatch") - self.check_all("fpformat") - self.check_all("ftplib") - self.check_all("getopt") - self.check_all("getpass") - self.check_all("gettext") - self.check_all("glob") - self.check_all("gzip") - self.check_all("heapq") - self.check_all("http.client") - self.check_all("ihooks") - self.check_all("io") - self.check_all("_pyio") - self.check_all("imaplib") - self.check_all("imghdr") - self.check_all("keyword") - self.check_all("linecache") - self.check_all("locale") - self.check_all("logging") - self.check_all("macpath") - self.check_all("macurl2path") - self.check_all("mailbox") - self.check_all("mailcap") - self.check_all("mhlib") - self.check_all("mimetypes") - self.check_all("multifile") - self.check_all("netrc") - self.check_all("nntplib") - self.check_all("ntpath") - self.check_all("opcode") - self.check_all("optparse") - self.check_all("os") - self.check_all("os2emxpath") - self.check_all("pdb") - self.check_all("pickle") - self.check_all("pickletools") - self.check_all("pipes") - self.check_all("poplib") - self.check_all("posixpath") - self.check_all("pprint") - self.check_all("profile") - self.check_all("pstats") - self.check_all("pty") - self.check_all("py_compile") - self.check_all("pyclbr") - self.check_all("quopri") - self.check_all("random") - self.check_all("re") - self.check_all("reprlib") - self.check_all("rlcompleter") - self.check_all("urllib.robotparser") - self.check_all("sched") - self.check_all("shelve") - self.check_all("shlex") - self.check_all("shutil") - self.check_all("smtpd") - self.check_all("smtplib") - self.check_all("sndhdr") - self.check_all("socket") - self.check_all("_strptime") - self.check_all("symtable") - self.check_all("tabnanny") - self.check_all("tarfile") - self.check_all("telnetlib") - self.check_all("tempfile") - self.check_all("test.support") - self.check_all("textwrap") - self.check_all("threading") - self.check_all("timeit") - self.check_all("tokenize") - self.check_all("traceback") - self.check_all("tty") - self.check_all("unittest") - self.check_all("uu") - self.check_all("warnings") - self.check_all("wave") - self.check_all("weakref") - self.check_all("webbrowser") - self.check_all("xdrlib") - self.check_all("zipfile") - # rlcompleter needs special consideration; it import readline which # initializes GNU readline which calls setlocale(LC_CTYPE, "")... :-( try: - self.check_all("rlcompleter") - finally: + import rlcompleter + import locale + except ImportError: + pass + else: + locale.setlocale(locale.LC_CTYPE, 'C') + + ignored = [] + failed_imports = [] + lib_dir = os.path.dirname(os.path.dirname(__file__)) + for path, modname in self.walk_modules(lib_dir, ""): + m = modname + blacklisted = False + while m: + if m in blacklist: + blacklisted = True + break + m = m.rpartition('.')[0] + if blacklisted: + continue + if support.verbose: + print(modname) try: - import locale - except ImportError: - pass - else: - locale.setlocale(locale.LC_CTYPE, 'C') + # This heuristic speeds up the process by removing, de facto, + # most test modules (and avoiding the auto-executing ones). + with open(path, "rb") as f: + if b"__all__" not in f.read(): + raise NoAll(modname) + self.check_all(modname) + except NoAll: + ignored.append(modname) + except FailedImport: + failed_imports.append(modname) + + if support.verbose: + print('Following modules have no __all__ and have been ignored:', + ignored) + print('Following modules failed to be imported:', failed_imports) def test_main(): - run_unittest(AllTest) + support.run_unittest(AllTest) if __name__ == "__main__": test_main() diff --git a/Misc/NEWS b/Misc/NEWS index 479c7e3fad2..1d4b6782327 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -271,6 +271,9 @@ Documentation Tests ----- +- Issue #7055: test___all__ now greedily detects all modules which have an + __all__ attribute, rather than using a hardcoded and incomplete list. + - Issue #7058: Added save/restore for argv and os.environ to runtest_inner in regrtest, with warnings if the called test modifies them.