mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 11:49:12 +00:00 
			
		
		
		
	svn+ssh://pythondev@svn.python.org/python/trunk ........ r74779 | michael.foord | 2009-09-13 11:13:36 -0500 (Sun, 13 Sep 2009) | 1 line Change to tutorial wording for reading text / binary files on Windows. Issue #6301. ........ r74780 | michael.foord | 2009-09-13 11:40:02 -0500 (Sun, 13 Sep 2009) | 1 line Objects that compare equal automatically pass or fail assertAlmostEqual and assertNotAlmostEqual tests on unittest.TestCase. Issue 6567. ........ r74781 | michael.foord | 2009-09-13 11:46:19 -0500 (Sun, 13 Sep 2009) | 1 line Note that sys._getframe is not guaranteed to exist in all implementations of Python, and a corresponding note in inspect.currentframe. Issue 6712. ........ r74782 | michael.foord | 2009-09-13 12:07:46 -0500 (Sun, 13 Sep 2009) | 1 line Tutorial tweaks. Issue 6849. ........ r74783 | michael.foord | 2009-09-13 12:28:35 -0500 (Sun, 13 Sep 2009) | 1 line unittest.TestLoader.loadTestsFromName honors the loader suiteClass attribute. Issue 6866. ........ r74784 | georg.brandl | 2009-09-13 13:15:07 -0500 (Sun, 13 Sep 2009) | 1 line Typo fix. ........ r74785 | michael.foord | 2009-09-13 14:07:03 -0500 (Sun, 13 Sep 2009) | 1 line Test discovery in unittest will only attempt to import modules that are importable; i.e. their names are valid Python identifiers. If an import fails during discovery this will be recorded as an error and test discovery will continue. Issue 6568. ........ r74786 | michael.foord | 2009-09-13 14:08:18 -0500 (Sun, 13 Sep 2009) | 1 line Remove an extraneous space in unittest documentation. ........ r74793 | georg.brandl | 2009-09-14 09:50:47 -0500 (Mon, 14 Sep 2009) | 1 line #6908: fix association of hashlib hash attributes. ........ r74795 | benjamin.peterson | 2009-09-14 22:36:26 -0500 (Mon, 14 Sep 2009) | 1 line Py_SetPythonHome uses static storage #6913 ........ r74811 | georg.brandl | 2009-09-15 15:26:59 -0500 (Tue, 15 Sep 2009) | 1 line Add Armin Ronacher. ........ r74860 | benjamin.peterson | 2009-09-16 21:46:54 -0500 (Wed, 16 Sep 2009) | 1 line kill bare except ........ r74861 | benjamin.peterson | 2009-09-16 22:18:28 -0500 (Wed, 16 Sep 2009) | 1 line pep 8 defaults ........ r74863 | benjamin.peterson | 2009-09-16 22:27:33 -0500 (Wed, 16 Sep 2009) | 1 line rationalize a bit ........ r74876 | georg.brandl | 2009-09-17 11:15:53 -0500 (Thu, 17 Sep 2009) | 1 line #6932: remove paragraph that advises relying on __del__ being called. ........ r74886 | benjamin.peterson | 2009-09-17 16:33:46 -0500 (Thu, 17 Sep 2009) | 1 line use macros ........ r74896 | georg.brandl | 2009-09-18 02:22:41 -0500 (Fri, 18 Sep 2009) | 1 line #6936: for interactive use, quit() is just fine. ........ r74901 | georg.brandl | 2009-09-18 04:14:52 -0500 (Fri, 18 Sep 2009) | 1 line #6905: use better exception messages in inspect when the argument is of the wrong type. ........ r74903 | georg.brandl | 2009-09-18 04:18:27 -0500 (Fri, 18 Sep 2009) | 1 line #6938: "ident" is always a string, so use a format code which works. ........ r74908 | georg.brandl | 2009-09-18 08:57:11 -0500 (Fri, 18 Sep 2009) | 1 line Use str.format() to fix beginner's mistake with %-style string formatting. ........ r74912 | georg.brandl | 2009-09-18 11:19:56 -0500 (Fri, 18 Sep 2009) | 1 line Optimize optimization and fix method name in docstring. ........ r74930 | georg.brandl | 2009-09-18 16:21:41 -0500 (Fri, 18 Sep 2009) | 1 line #6925: rewrite docs for locals() and vars() a bit. ........ r74933 | georg.brandl | 2009-09-18 16:35:59 -0500 (Fri, 18 Sep 2009) | 1 line #6930: clarify description about byteorder handling in UTF decoder routines. ........ r74943 | georg.brandl | 2009-09-19 02:35:07 -0500 (Sat, 19 Sep 2009) | 1 line #6944: the argument to PyArg_ParseTuple should be a tuple, otherwise a SystemError is set. Also clean up another usage of PyArg_ParseTuple. ........ r74946 | georg.brandl | 2009-09-19 03:43:16 -0500 (Sat, 19 Sep 2009) | 1 line Update bug tracker reference. ........ r74952 | georg.brandl | 2009-09-19 05:42:34 -0500 (Sat, 19 Sep 2009) | 1 line #6946: fix duplicate index entries for datetime classes. ........ r74953 | georg.brandl | 2009-09-19 07:04:16 -0500 (Sat, 19 Sep 2009) | 1 line Fix references to threading.enumerate(). ........ r74954 | georg.brandl | 2009-09-19 08:13:56 -0500 (Sat, 19 Sep 2009) | 1 line Add Doug. ........ r74955 | georg.brandl | 2009-09-19 08:20:49 -0500 (Sat, 19 Sep 2009) | 1 line Add Mark Summerfield. ........ r75015 | georg.brandl | 2009-09-22 05:55:08 -0500 (Tue, 22 Sep 2009) | 1 line Fix encoding name. ........ r75019 | vinay.sajip | 2009-09-22 12:23:41 -0500 (Tue, 22 Sep 2009) | 1 line Fixed a typo, and added sections on optimization and using arbitrary objects as messages. ........ r75032 | benjamin.peterson | 2009-09-22 17:15:28 -0500 (Tue, 22 Sep 2009) | 1 line fix typos/rephrase ........ r75068 | benjamin.peterson | 2009-09-25 21:57:59 -0500 (Fri, 25 Sep 2009) | 1 line comment out ugly xxx ........ r75076 | vinay.sajip | 2009-09-26 09:53:32 -0500 (Sat, 26 Sep 2009) | 1 line Tidied up name of parameter in StreamHandler ........ r75095 | michael.foord | 2009-09-27 14:15:41 -0500 (Sun, 27 Sep 2009) | 1 line Test creation moved from TestProgram.parseArgs to TestProgram.createTests exclusively. Issue 6956. ........ r75098 | michael.foord | 2009-09-27 15:08:23 -0500 (Sun, 27 Sep 2009) | 1 line Documentation improvement for load_tests protocol in unittest. Issue 6515. ........ r75102 | skip.montanaro | 2009-09-27 21:12:27 -0500 (Sun, 27 Sep 2009) | 3 lines Patch from Thomas Barr so that csv.Sniffer will set doublequote property. Closes issue 6606. ........ r75129 | vinay.sajip | 2009-09-29 02:08:54 -0500 (Tue, 29 Sep 2009) | 1 line Issue #7014: logging: Improved IronPython 2.6 compatibility. ........ r75139 | raymond.hettinger | 2009-09-29 13:53:24 -0500 (Tue, 29 Sep 2009) | 3 lines Issue 7008: Better document str.title and show how to work around the apostrophe problem. ........ r75230 | benjamin.peterson | 2009-10-04 08:38:38 -0500 (Sun, 04 Oct 2009) | 1 line test logging ........
		
			
				
	
	
		
			260 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Loading unittests."""
 | 
						|
 | 
						|
import os
 | 
						|
import re
 | 
						|
import sys
 | 
						|
import traceback
 | 
						|
import types
 | 
						|
 | 
						|
from fnmatch import fnmatch
 | 
						|
 | 
						|
from . import case, suite, util
 | 
						|
 | 
						|
 | 
						|
# what about .pyc or .pyo (etc)
 | 
						|
# we would need to avoid loading the same tests multiple times
 | 
						|
# from '.py', '.pyc' *and* '.pyo'
 | 
						|
VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE)
 | 
						|
 | 
						|
 | 
						|
def _make_failed_import_test(name, suiteClass):
 | 
						|
    message = 'Failed to import test module: %s' % name
 | 
						|
    if hasattr(traceback, 'format_exc'):
 | 
						|
        # Python 2.3 compatibility
 | 
						|
        # format_exc returns two frames of discover.py as well
 | 
						|
        message += '\n%s' % traceback.format_exc()
 | 
						|
 | 
						|
    def testImportFailure(self):
 | 
						|
        raise ImportError(message)
 | 
						|
    attrs = {name: testImportFailure}
 | 
						|
    ModuleImportFailure = type('ModuleImportFailure', (case.TestCase,), attrs)
 | 
						|
    return suiteClass((ModuleImportFailure(name),))
 | 
						|
 | 
						|
 | 
						|
class TestLoader(object):
 | 
						|
    """
 | 
						|
    This class is responsible for loading tests according to various criteria
 | 
						|
    and returning them wrapped in a TestSuite
 | 
						|
    """
 | 
						|
    testMethodPrefix = 'test'
 | 
						|
    sortTestMethodsUsing = staticmethod(util.three_way_cmp)
 | 
						|
    suiteClass = suite.TestSuite
 | 
						|
    _top_level_dir = None
 | 
						|
 | 
						|
    def loadTestsFromTestCase(self, testCaseClass):
 | 
						|
        """Return a suite of all tests cases contained in testCaseClass"""
 | 
						|
        if issubclass(testCaseClass, suite.TestSuite):
 | 
						|
            raise TypeError("Test cases should not be derived from TestSuite." \
 | 
						|
                                " Maybe you meant to derive from TestCase?")
 | 
						|
        testCaseNames = self.getTestCaseNames(testCaseClass)
 | 
						|
        if not testCaseNames and hasattr(testCaseClass, 'runTest'):
 | 
						|
            testCaseNames = ['runTest']
 | 
						|
        loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
 | 
						|
        return loaded_suite
 | 
						|
 | 
						|
    def loadTestsFromModule(self, module, use_load_tests=True):
 | 
						|
        """Return a suite of all tests cases contained in the given module"""
 | 
						|
        tests = []
 | 
						|
        for name in dir(module):
 | 
						|
            obj = getattr(module, name)
 | 
						|
            if isinstance(obj, type) and issubclass(obj, case.TestCase):
 | 
						|
                tests.append(self.loadTestsFromTestCase(obj))
 | 
						|
 | 
						|
        load_tests = getattr(module, 'load_tests', None)
 | 
						|
        if use_load_tests and load_tests is not None:
 | 
						|
            return load_tests(self, tests, None)
 | 
						|
        return self.suiteClass(tests)
 | 
						|
 | 
						|
    def loadTestsFromName(self, name, module=None):
 | 
						|
        """Return a suite of all tests cases given a string specifier.
 | 
						|
 | 
						|
        The name may resolve either to a module, a test case class, a
 | 
						|
        test method within a test case class, or a callable object which
 | 
						|
        returns a TestCase or TestSuite instance.
 | 
						|
 | 
						|
        The method optionally resolves the names relative to a given module.
 | 
						|
        """
 | 
						|
        parts = name.split('.')
 | 
						|
        if module is None:
 | 
						|
            parts_copy = parts[:]
 | 
						|
            while parts_copy:
 | 
						|
                try:
 | 
						|
                    module = __import__('.'.join(parts_copy))
 | 
						|
                    break
 | 
						|
                except ImportError:
 | 
						|
                    del parts_copy[-1]
 | 
						|
                    if not parts_copy:
 | 
						|
                        raise
 | 
						|
            parts = parts[1:]
 | 
						|
        obj = module
 | 
						|
        for part in parts:
 | 
						|
            parent, obj = obj, getattr(obj, part)
 | 
						|
 | 
						|
        if isinstance(obj, types.ModuleType):
 | 
						|
            return self.loadTestsFromModule(obj)
 | 
						|
        elif isinstance(obj, type) and issubclass(obj, case.TestCase):
 | 
						|
            return self.loadTestsFromTestCase(obj)
 | 
						|
        elif (isinstance(obj, types.FunctionType) and
 | 
						|
              isinstance(parent, type) and
 | 
						|
              issubclass(parent, case.TestCase)):
 | 
						|
            name = obj.__name__
 | 
						|
            inst = parent(name)
 | 
						|
            # static methods follow a different path
 | 
						|
            if not isinstance(getattr(inst, name), types.FunctionType):
 | 
						|
                return self.suiteClass([inst])
 | 
						|
        elif isinstance(obj, suite.TestSuite):
 | 
						|
            return obj
 | 
						|
        if hasattr(obj, '__call__'):
 | 
						|
            test = obj()
 | 
						|
            if isinstance(test, suite.TestSuite):
 | 
						|
                return test
 | 
						|
            elif isinstance(test, case.TestCase):
 | 
						|
                return self.suiteClass([test])
 | 
						|
            else:
 | 
						|
                raise TypeError("calling %s returned %s, not a test" %
 | 
						|
                                (obj, test))
 | 
						|
        else:
 | 
						|
            raise TypeError("don't know how to make test from: %s" % obj)
 | 
						|
 | 
						|
    def loadTestsFromNames(self, names, module=None):
 | 
						|
        """Return a suite of all tests cases found using the given sequence
 | 
						|
        of string specifiers. See 'loadTestsFromName()'.
 | 
						|
        """
 | 
						|
        suites = [self.loadTestsFromName(name, module) for name in names]
 | 
						|
        return self.suiteClass(suites)
 | 
						|
 | 
						|
    def getTestCaseNames(self, testCaseClass):
 | 
						|
        """Return a sorted sequence of method names found within testCaseClass
 | 
						|
        """
 | 
						|
        def isTestMethod(attrname, testCaseClass=testCaseClass,
 | 
						|
                         prefix=self.testMethodPrefix):
 | 
						|
            return attrname.startswith(prefix) and \
 | 
						|
                hasattr(getattr(testCaseClass, attrname), '__call__')
 | 
						|
        testFnNames = testFnNames = list(filter(isTestMethod,
 | 
						|
                                                dir(testCaseClass)))
 | 
						|
        if self.sortTestMethodsUsing:
 | 
						|
            testFnNames.sort(key=util.CmpToKey(self.sortTestMethodsUsing))
 | 
						|
        return testFnNames
 | 
						|
 | 
						|
    def discover(self, start_dir, pattern='test*.py', top_level_dir=None):
 | 
						|
        """Find and return all test modules from the specified start
 | 
						|
        directory, recursing into subdirectories to find them. Only test files
 | 
						|
        that match the pattern will be loaded. (Using shell style pattern
 | 
						|
        matching.)
 | 
						|
 | 
						|
        All test modules must be importable from the top level of the project.
 | 
						|
        If the start directory is not the top level directory then the top
 | 
						|
        level directory must be specified separately.
 | 
						|
 | 
						|
        If a test package name (directory with '__init__.py') matches the
 | 
						|
        pattern then the package will be checked for a 'load_tests' function. If
 | 
						|
        this exists then it will be called with loader, tests, pattern.
 | 
						|
 | 
						|
        If load_tests exists then discovery does  *not* recurse into the package,
 | 
						|
        load_tests is responsible for loading all tests in the package.
 | 
						|
 | 
						|
        The pattern is deliberately not stored as a loader attribute so that
 | 
						|
        packages can continue discovery themselves. top_level_dir is stored so
 | 
						|
        load_tests does not need to pass this argument in to loader.discover().
 | 
						|
        """
 | 
						|
        if top_level_dir is None and self._top_level_dir is not None:
 | 
						|
            # make top_level_dir optional if called from load_tests in a package
 | 
						|
            top_level_dir = self._top_level_dir
 | 
						|
        elif top_level_dir is None:
 | 
						|
            top_level_dir = start_dir
 | 
						|
 | 
						|
        top_level_dir = os.path.abspath(os.path.normpath(top_level_dir))
 | 
						|
        start_dir = os.path.abspath(os.path.normpath(start_dir))
 | 
						|
 | 
						|
        if not top_level_dir in sys.path:
 | 
						|
            # all test modules must be importable from the top level directory
 | 
						|
            sys.path.append(top_level_dir)
 | 
						|
        self._top_level_dir = top_level_dir
 | 
						|
 | 
						|
        if start_dir != top_level_dir and not os.path.isfile(os.path.join(start_dir, '__init__.py')):
 | 
						|
            # what about __init__.pyc or pyo (etc)
 | 
						|
            raise ImportError('Start directory is not importable: %r' % start_dir)
 | 
						|
 | 
						|
        tests = list(self._find_tests(start_dir, pattern))
 | 
						|
        return self.suiteClass(tests)
 | 
						|
 | 
						|
    def _get_name_from_path(self, path):
 | 
						|
        path = os.path.splitext(os.path.normpath(path))[0]
 | 
						|
 | 
						|
        _relpath = os.path.relpath(path, self._top_level_dir)
 | 
						|
        assert not os.path.isabs(_relpath), "Path must be within the project"
 | 
						|
        assert not _relpath.startswith('..'), "Path must be within the project"
 | 
						|
 | 
						|
        name = _relpath.replace(os.path.sep, '.')
 | 
						|
        return name
 | 
						|
 | 
						|
    def _get_module_from_name(self, name):
 | 
						|
        __import__(name)
 | 
						|
        return sys.modules[name]
 | 
						|
 | 
						|
    def _find_tests(self, start_dir, pattern):
 | 
						|
        """Used by discovery. Yields test suites it loads."""
 | 
						|
        paths = os.listdir(start_dir)
 | 
						|
 | 
						|
        for path in paths:
 | 
						|
            full_path = os.path.join(start_dir, path)
 | 
						|
            if os.path.isfile(full_path):
 | 
						|
                if not VALID_MODULE_NAME.match(path):
 | 
						|
                    # valid Python identifiers only
 | 
						|
                    continue
 | 
						|
 | 
						|
                if fnmatch(path, pattern):
 | 
						|
                    # if the test file matches, load it
 | 
						|
                    name = self._get_name_from_path(full_path)
 | 
						|
                    try:
 | 
						|
                        module = self._get_module_from_name(name)
 | 
						|
                    except:
 | 
						|
                        yield _make_failed_import_test(name, self.suiteClass)
 | 
						|
                    else:
 | 
						|
                        yield self.loadTestsFromModule(module)
 | 
						|
            elif os.path.isdir(full_path):
 | 
						|
                if not os.path.isfile(os.path.join(full_path, '__init__.py')):
 | 
						|
                    continue
 | 
						|
 | 
						|
                load_tests = None
 | 
						|
                tests = None
 | 
						|
                if fnmatch(path, pattern):
 | 
						|
                    # only check load_tests if the package directory itself matches the filter
 | 
						|
                    name = self._get_name_from_path(full_path)
 | 
						|
                    package = self._get_module_from_name(name)
 | 
						|
                    load_tests = getattr(package, 'load_tests', None)
 | 
						|
                    tests = self.loadTestsFromModule(package, use_load_tests=False)
 | 
						|
 | 
						|
                if load_tests is None:
 | 
						|
                    if tests is not None:
 | 
						|
                        # tests loaded from package file
 | 
						|
                        yield tests
 | 
						|
                    # recurse into the package
 | 
						|
                    for test in self._find_tests(full_path, pattern):
 | 
						|
                        yield test
 | 
						|
                else:
 | 
						|
                    yield load_tests(self, tests, pattern)
 | 
						|
 | 
						|
defaultTestLoader = TestLoader()
 | 
						|
 | 
						|
 | 
						|
def _makeLoader(prefix, sortUsing, suiteClass=None):
 | 
						|
    loader = TestLoader()
 | 
						|
    loader.sortTestMethodsUsing = sortUsing
 | 
						|
    loader.testMethodPrefix = prefix
 | 
						|
    if suiteClass:
 | 
						|
        loader.suiteClass = suiteClass
 | 
						|
    return loader
 | 
						|
 | 
						|
def getTestCaseNames(testCaseClass, prefix, sortUsing=util.three_way_cmp):
 | 
						|
    return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
 | 
						|
 | 
						|
def makeSuite(testCaseClass, prefix='test', sortUsing=util.three_way_cmp,
 | 
						|
              suiteClass=suite.TestSuite):
 | 
						|
    return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(
 | 
						|
        testCaseClass)
 | 
						|
 | 
						|
def findTestCases(module, prefix='test', sortUsing=util.three_way_cmp,
 | 
						|
                  suiteClass=suite.TestSuite):
 | 
						|
    return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(\
 | 
						|
        module)
 |