root/galaxy-central/eggs/nose-0.11.1-py2.6.egg/nose/loader.py @ 3

リビジョン 3, 22.8 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""
2Test Loader
3-----------
4
5nose's test loader implements the same basic functionality as its
6superclass, unittest.TestLoader, but extends it by more liberal
7interpretations of what may be a test and how a test may be named.
8"""
9from __future__ import generators
10
11import logging
12import os
13import sys
14import unittest
15from inspect import isfunction, ismethod
16from nose.case import FunctionTestCase, MethodTestCase
17from nose.failure import Failure
18from nose.config import Config
19from nose.importer import Importer, add_path, remove_path
20from nose.selector import defaultSelector, TestAddress
21from nose.util import cmp_lineno, getpackage, isclass, isgenerator, ispackage, \
22    match_last, resolve_name, transplant_func, transplant_class, test_address
23from nose.suite import ContextSuiteFactory, ContextList, LazySuite
24
25
26log = logging.getLogger(__name__)
27#log.setLevel(logging.DEBUG)
28
29# for efficiency and easier mocking
30op_normpath = os.path.normpath
31op_abspath = os.path.abspath
32op_join = os.path.join
33op_isdir = os.path.isdir
34op_isfile = os.path.isfile
35
36
37__all__ = ['TestLoader', 'defaultTestLoader']
38
39
40class TestLoader(unittest.TestLoader):
41    """Test loader that extends unittest.TestLoader to:
42
43    * Load tests from test-like functions and classes that are not
44      unittest.TestCase subclasses
45    * Find and load test modules in a directory
46    * Support tests that are generators
47    * Support easy extensions of or changes to that behavior through plugins
48    """
49    config = None
50    importer = None
51    workingDir = None
52    selector = None
53    suiteClass = None
54   
55    def __init__(self, config=None, importer=None, workingDir=None,
56                 selector=None):
57        """Initialize a test loader.
58
59        Parameters (all optional):
60
61        * config: provide a `nose.config.Config`_ or other config class
62          instance; if not provided a `nose.config.Config`_ with
63          default values is used.         
64        * importer: provide an importer instance that implements
65          `importFromPath`. If not provided, a
66          `nose.importer.Importer`_ is used.
67        * workingDir: the directory to which file and module names are
68          relative. If not provided, assumed to be the current working
69          directory.
70        * selector: a selector class or instance. If a class is
71          provided, it will be instantiated with one argument, the
72          current config. If not provided, a `nose.selector.Selector`_
73          is used.
74        """
75        if config is None:
76            config = Config()
77        if importer is None:
78            importer = Importer(config=config)
79        if workingDir is None:
80            workingDir = config.workingDir
81        if selector is None:
82            selector = defaultSelector(config)
83        elif isclass(selector):
84            selector = selector(config)
85        self.config = config
86        self.importer = importer
87        self.workingDir = op_normpath(op_abspath(workingDir))
88        self.selector = selector
89        if config.addPaths:
90            add_path(workingDir, config)       
91        self.suiteClass = ContextSuiteFactory(config=config)
92        unittest.TestLoader.__init__(self)     
93
94    def getTestCaseNames(self, testCaseClass):
95        """Override to select with selector, unless
96        config.getTestCaseNamesCompat is True
97        """
98        if self.config.getTestCaseNamesCompat:
99            return unittest.TestLoader.getTestCaseNames(self, testCaseClass)
100       
101        def wanted(attr, cls=testCaseClass, sel=self.selector):
102            item = getattr(cls, attr, None)
103            if not ismethod(item):
104                return False
105            return sel.wantMethod(item)
106        cases = filter(wanted, dir(testCaseClass))
107        for base in testCaseClass.__bases__:
108            for case in self.getTestCaseNames(base):
109                if case not in cases:
110                    cases.append(case)
111        # add runTest if nothing else picked
112        if not cases and hasattr(testCaseClass, 'runTest'):
113            cases = ['runTest']
114        if self.sortTestMethodsUsing:
115            cases.sort(self.sortTestMethodsUsing)
116        return cases
117
118    def loadTestsFromDir(self, path):
119        """Load tests from the directory at path. This is a generator
120        -- each suite of tests from a module or other file is yielded
121        and is expected to be executed before the next file is
122        examined.
123        """       
124        log.debug("load from dir %s", path)
125        plugins = self.config.plugins
126        plugins.beforeDirectory(path)
127        if self.config.addPaths:
128            paths_added = add_path(path, self.config)
129
130        entries = os.listdir(path)
131        entries.sort(lambda a, b: match_last(a, b, self.config.testMatch))
132        for entry in entries:
133            # this hard-coded initial-dot test will be removed:
134            # http://code.google.com/p/python-nose/issues/detail?id=82
135            if entry.startswith('.'):
136                continue
137            entry_path = op_abspath(op_join(path, entry))
138            is_file = op_isfile(entry_path)
139            wanted = False
140            if is_file:
141                is_dir = False
142                wanted = self.selector.wantFile(entry_path)
143            else:
144                is_dir = op_isdir(entry_path)
145                if is_dir:
146                    # this hard-coded initial-underscore test will be removed:
147                    # http://code.google.com/p/python-nose/issues/detail?id=82
148                    if entry.startswith('_'):
149                        continue
150                    wanted = self.selector.wantDirectory(entry_path)
151            is_package = ispackage(entry_path)
152            if wanted:
153                if is_file:
154                    plugins.beforeContext()
155                    if entry.endswith('.py'):
156                        yield self.loadTestsFromName(
157                            entry_path, discovered=True)
158                    else:
159                        yield self.loadTestsFromFile(entry_path)
160                    plugins.afterContext()
161                elif is_package:
162                    # Load the entry as a package: given the full path,
163                    # loadTestsFromName() will figure it out
164                    yield self.loadTestsFromName(
165                        entry_path, discovered=True)
166                else:
167                    # Another test dir in this one: recurse lazily
168                    yield self.suiteClass(
169                        lambda: self.loadTestsFromDir(entry_path))
170        tests = []
171        for test in plugins.loadTestsFromDir(path):
172            tests.append(test)
173        # TODO: is this try/except needed?
174        try:
175            if tests:
176                yield self.suiteClass(tests)
177        except (KeyboardInterrupt, SystemExit):
178            raise
179        except:
180            yield self.suiteClass([Failure(*sys.exc_info())])
181       
182        # pop paths
183        if self.config.addPaths:
184            map(remove_path, paths_added)
185        plugins.afterDirectory(path)
186
187    def loadTestsFromFile(self, filename):
188        """Load tests from a non-module file. Default is to raise a
189        ValueError; plugins may implement `loadTestsFromFile` to
190        provide a list of tests loaded from the file.
191        """
192        log.debug("Load from non-module file %s", filename)
193        try:
194            tests = [test for test in
195                     self.config.plugins.loadTestsFromFile(filename)]
196            if tests:
197                # Plugins can yield False to indicate that they were
198                # unable to load tests from a file, but it was not an
199                # error -- the file just had no tests to load.
200                tests = filter(None, tests)
201                return self.suiteClass(tests)
202            else:
203                # Nothing was able to even try to load from this file
204                open(filename, 'r').close() # trigger os error
205                raise ValueError("Unable to load tests from file %s"
206                                 % filename)
207        except (KeyboardInterrupt, SystemExit):
208            raise
209        except:
210            exc = sys.exc_info()
211            return self.suiteClass(
212                [Failure(exc[0], exc[1], exc[2],
213                         address=(filename, None, None))])
214
215    def loadTestsFromGenerator(self, generator, module):
216        """Lazy-load tests from a generator function. The generator function
217        may yield either:
218
219        * a callable, or
220        * a function name resolvable within the same module
221        """
222        def generate(g=generator, m=module):
223            try:
224                for test in g():
225                    test_func, arg = self.parseGeneratedTest(test)
226                    if not callable(test_func):
227                        test_func = getattr(m, test_func)
228                    yield FunctionTestCase(test_func, arg=arg, descriptor=g)
229            except KeyboardInterrupt:
230                raise
231            except:
232                exc = sys.exc_info()
233                yield Failure(exc[0], exc[1], exc[2],
234                              address=test_address(generator))
235        return self.suiteClass(generate, context=generator, can_split=False)
236
237    def loadTestsFromGeneratorMethod(self, generator, cls):
238        """Lazy-load tests from a generator method.
239
240        This is more complicated than loading from a generator function,
241        since a generator method may yield:
242
243        * a function
244        * a bound or unbound method, or
245        * a method name
246        """
247        # convert the unbound generator method
248        # into a bound method so it can be called below
249        cls = generator.im_class
250        inst = cls()
251        method = generator.__name__
252        generator = getattr(inst, method)
253
254        def generate(g=generator, c=cls):
255            try:
256                for test in g():
257                    test_func, arg = self.parseGeneratedTest(test)
258                    if not callable(test_func):
259                        test_func = getattr(c, test_func)
260                    if ismethod(test_func):
261                        yield MethodTestCase(test_func, arg=arg, descriptor=g)
262                    elif isfunction(test_func):
263                        # In this case we're forcing the 'MethodTestCase'
264                        # to run the inline function as its test call,
265                        # but using the generator method as the 'method of
266                        # record' (so no need to pass it as the descriptor)
267                        yield MethodTestCase(g, test=test_func, arg=arg)
268                    else:
269                        yield Failure(
270                            TypeError,
271                            "%s is not a function or method" % test_func)
272            except KeyboardInterrupt:
273                raise
274            except:
275                exc = sys.exc_info()
276                yield Failure(exc[0], exc[1], exc[2],
277                              address=test_address(generator))
278        return self.suiteClass(generate, context=generator, can_split=False)
279
280    def loadTestsFromModule(self, module, path=None, discovered=False):
281        """Load all tests from module and return a suite containing
282        them. If the module has been discovered and is not test-like,
283        the suite will be empty by default, though plugins may add
284        their own tests.
285        """
286        log.debug("Load from module %s", module)
287        tests = []
288        test_classes = []
289        test_funcs = []
290        # For *discovered* modules, we only load tests when the module looks
291        # testlike. For modules we've been directed to load, we always
292        # look for tests. (discovered is set to True by loadTestsFromDir)
293        if not discovered or self.selector.wantModule(module):
294            for item in dir(module):
295                test = getattr(module, item, None)
296                # print "Check %s (%s) in %s" % (item, test, module.__name__)
297                if isclass(test):
298                    if self.selector.wantClass(test):
299                        test_classes.append(test)
300                elif isfunction(test) and self.selector.wantFunction(test):
301                    test_funcs.append(test)
302            test_classes.sort(lambda a, b: cmp(a.__name__, b.__name__))
303            test_funcs.sort(cmp_lineno)
304            tests = map(lambda t: self.makeTest(t, parent=module),
305                        test_classes + test_funcs)
306
307        # Now, descend into packages
308        # FIXME can or should this be lazy?
309        # is this syntax 2.2 compatible?
310        module_paths = getattr(module, '__path__', [])
311        if path:
312            path = os.path.realpath(path)
313        for module_path in module_paths:
314            if (self.config.traverseNamespace or not path) or \
315                    os.path.realpath(module_path).startswith(path):
316                tests.extend(self.loadTestsFromDir(module_path))
317           
318        for test in self.config.plugins.loadTestsFromModule(module, path):
319            tests.append(test)
320
321        return self.suiteClass(ContextList(tests, context=module))
322   
323    def loadTestsFromName(self, name, module=None, discovered=False):
324        """Load tests from the entity with the given name.
325
326        The name may indicate a file, directory, module, or any object
327        within a module. See `nose.util.split_test_name` for details on
328        test name parsing.
329        """
330        # FIXME refactor this method into little bites?
331        log.debug("load from %s (%s)", name, module)
332       
333        suite = self.suiteClass
334
335        # give plugins first crack
336        plug_tests = self.config.plugins.loadTestsFromName(name, module)
337        if plug_tests:
338            return suite(plug_tests)
339       
340        addr = TestAddress(name, workingDir=self.workingDir)
341        if module:
342            # Two cases:
343            #  name is class.foo
344            #    The addr will be incorrect, since it thinks class.foo is
345            #    a dotted module name. It's actually a dotted attribute
346            #    name. In this case we want to use the full submitted
347            #    name as the name to load from the module.
348            #  name is module:class.foo
349            #    The addr will be correct. The part we want is the part after
350            #    the :, which is in addr.call.
351            if addr.call:
352                name = addr.call
353            parent, obj = self.resolve(name, module)
354            if (isclass(parent)
355                and getattr(parent, '__module__', None) != module.__name__):
356                parent = transplant_class(parent, module.__name__)
357                obj = getattr(parent, obj.__name__)
358            log.debug("parent %s obj %s module %s", parent, obj, module)
359            if isinstance(obj, Failure):
360                return suite([obj])
361            else:
362                return suite(ContextList([self.makeTest(obj, parent)],
363                                         context=parent))
364        else:
365            if addr.module:
366                try:
367                    if addr.filename is None:
368                        module = resolve_name(addr.module)
369                    else:
370                        self.config.plugins.beforeImport(
371                            addr.filename, addr.module)
372                        # FIXME: to support module.name names,
373                        # do what resolve-name does and keep trying to
374                        # import, popping tail of module into addr.call,
375                        # until we either get an import or run out of
376                        # module parts
377                        try:
378                            module = self.importer.importFromPath(
379                                addr.filename, addr.module)
380                        finally:
381                            self.config.plugins.afterImport(
382                                addr.filename, addr.module)
383                except (KeyboardInterrupt, SystemExit):
384                    raise
385                except:
386                    exc = sys.exc_info()
387                    return suite([Failure(exc[0], exc[1], exc[2],
388                                          address=addr.totuple())])
389                if addr.call:
390                    return self.loadTestsFromName(addr.call, module)
391                else:
392                    return self.loadTestsFromModule(
393                        module, addr.filename,
394                        discovered=discovered)
395            elif addr.filename:
396                path = addr.filename
397                if addr.call:
398                    package = getpackage(path)
399                    if package is None:
400                        return suite([
401                            Failure(ValueError,
402                                    "Can't find callable %s in file %s: "
403                                    "file is not a python module" %
404                                    (addr.call, path),
405                                    address=addr.totuple())])
406                    return self.loadTestsFromName(addr.call, module=package)
407                else:
408                    if op_isdir(path):
409                        # In this case we *can* be lazy since we know
410                        # that each module in the dir will be fully
411                        # loaded before its tests are executed; we
412                        # also know that we're not going to be asked
413                        # to load from . and ./some_module.py *as part
414                        # of this named test load*
415                        return LazySuite(
416                            lambda: self.loadTestsFromDir(path))
417                    elif op_isfile(path):
418                        return self.loadTestsFromFile(path)
419                    else:
420                        return suite([
421                                Failure(OSError, "No such file %s" % path,
422                                        address=addr.totuple())])
423            else:
424                # just a function? what to do? I think it can only be
425                # handled when module is not None
426                return suite([
427                    Failure(ValueError, "Unresolvable test name %s" % name,
428                            address=addr.totuple())])
429
430    def loadTestsFromNames(self, names, module=None):
431        """Load tests from all names, returning a suite containing all
432        tests.
433        """
434        plug_res = self.config.plugins.loadTestsFromNames(names, module)
435        if plug_res:
436            suite, names = plug_res
437            if suite:
438                return self.suiteClass([
439                    self.suiteClass(suite),
440                    unittest.TestLoader.loadTestsFromNames(self, names, module)
441                    ])
442        return unittest.TestLoader.loadTestsFromNames(self, names, module)
443
444    def loadTestsFromTestCase(self, testCaseClass):
445        """Load tests from a unittest.TestCase subclass.
446        """
447        cases = []
448        plugins = self.config.plugins
449        for case in plugins.loadTestsFromTestCase(testCaseClass):
450            cases.append(case)
451        # For efficiency in the most common case, just call and return from
452        # super. This avoids having to extract cases and rebuild a context
453        # suite when there are no plugin-contributed cases.
454        if not cases:
455            return super(TestLoader, self).loadTestsFromTestCase(testCaseClass)
456        cases.extend(
457            [case for case in
458             super(TestLoader, self).loadTestsFromTestCase(testCaseClass)])
459        return self.suiteClass(cases)
460   
461    def loadTestsFromTestClass(self, cls):
462        """Load tests from a test class that is *not* a unittest.TestCase
463        subclass.
464
465        In this case, we can't depend on the class's `__init__` taking method
466        name arguments, so we have to compose a MethodTestCase for each
467        method in the class that looks testlike.
468        """
469        def wanted(attr, cls=cls, sel=self.selector):
470            item = getattr(cls, attr, None)
471            if not ismethod(item):
472                return False
473            return sel.wantMethod(item)
474        cases = [self.makeTest(getattr(cls, case), cls)
475                 for case in filter(wanted, dir(cls))]
476        for test in self.config.plugins.loadTestsFromTestClass(cls):
477            cases.append(test)
478        return self.suiteClass(ContextList(cases, context=cls))
479
480    def makeTest(self, obj, parent=None):
481        """Given a test object and its parent, return a test case
482        or test suite.
483        """
484        plug_tests = []
485        try:
486            addr = test_address(obj)
487        except KeyboardInterrupt:
488            raise
489        except:
490            addr = None
491        for test in self.config.plugins.makeTest(obj, parent):
492            plug_tests.append(test)
493        # TODO: is this try/except needed?
494        try:
495            if plug_tests:
496                return self.suiteClass(plug_tests)
497        except (KeyboardInterrupt, SystemExit):
498            raise
499        except:
500            exc = sys.exc_info()
501            return Failure(exc[0], exc[1], exc[2], address=addr)
502       
503        if isinstance(obj, unittest.TestCase):
504            return obj
505        elif isclass(obj):
506            if parent and obj.__module__ != parent.__name__:
507                obj = transplant_class(obj, parent.__name__)
508            if issubclass(obj, unittest.TestCase):
509                return self.loadTestsFromTestCase(obj)
510            else:
511                return self.loadTestsFromTestClass(obj)
512        elif ismethod(obj):
513            if parent is None:
514                parent = obj.__class__
515            if issubclass(parent, unittest.TestCase):
516                return parent(obj.__name__)
517            else:
518                if isgenerator(obj):
519                    return self.loadTestsFromGeneratorMethod(obj, parent)
520                else:
521                    return MethodTestCase(obj)
522        elif isfunction(obj):
523            if parent and obj.__module__ != parent.__name__:
524                obj = transplant_func(obj, parent.__name__)
525            if isgenerator(obj):
526                return self.loadTestsFromGenerator(obj, parent)
527            else:
528                return FunctionTestCase(obj)
529        else:
530            return Failure(TypeError,
531                           "Can't make a test from %s" % obj,
532                           address=addr)
533
534    def resolve(self, name, module):
535        """Resolve name within module
536        """
537        obj = module
538        parts = name.split('.')
539        for part in parts:
540            parent, obj = obj, getattr(obj, part, None)
541        if obj is None:
542            # no such test
543            obj = Failure(ValueError, "No such test %s" % name)
544        return parent, obj
545
546    def parseGeneratedTest(self, test):
547        """Given the yield value of a test generator, return a func and args.
548
549        This is used in the two loadTestsFromGenerator* methods.
550
551        """
552        if not isinstance(test, tuple):         # yield test
553            test_func, arg = (test, tuple())
554        elif len(test) == 1:                    # yield (test,)
555            test_func, arg = (test[0], tuple())
556        else:                                   # yield test, foo, bar, ...
557            assert len(test) > 1 # sanity check
558            test_func, arg = (test[0], test[1:])
559        return test_func, arg
560
561defaultTestLoader = TestLoader
562
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。