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

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

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

行番号 
1"""
2ErrorClass Plugins
3------------------
4
5ErrorClass plugins provide an easy way to add support for custom
6handling of particular classes of exceptions.
7
8An ErrorClass plugin defines one or more ErrorClasses and how each is
9handled and reported on. Each error class is stored in a different
10attribute on the result, and reported separately. Each error class must
11indicate the exceptions that fall under that class, the label to use
12for reporting, and whether exceptions of the class should be
13considered as failures for the whole test run.
14
15ErrorClasses use a declarative syntax. Assign an ErrorClass to the
16attribute you wish to add to the result object, defining the
17exceptions, label and isfailure attributes. For example, to declare an
18ErrorClassPlugin that defines TodoErrors (and subclasses of TodoError)
19as an error class with the label 'TODO' that is considered a failure,
20do this:
21
22    >>> class Todo(Exception):
23    ...     pass
24    >>> class TodoError(ErrorClassPlugin):
25    ...     todo = ErrorClass(Todo, label='TODO', isfailure=True)
26
27The MetaErrorClass metaclass translates the ErrorClass declarations
28into the tuples used by the error handling and reporting functions in
29the result. This is an internal format and subject to change; you
30should always use the declarative syntax for attaching ErrorClasses to
31an ErrorClass plugin.
32   
33    >>> TodoError.errorClasses # doctest: +ELLIPSIS
34    ((<class ...Todo...>, ('todo', 'TODO', True)),)
35
36Let's see the plugin in action. First some boilerplate.
37
38    >>> import sys
39    >>> import unittest
40    >>> buf = unittest._WritelnDecorator(sys.stdout)
41
42Now define a test case that raises a Todo.
43
44    >>> class TestTodo(unittest.TestCase):
45    ...     def runTest(self):
46    ...         raise Todo("I need to test something")
47    >>> case = TestTodo()
48
49Prepare the result using our plugin. Normally this happens during the
50course of test execution within nose -- you won't be doing this
51yourself. For the purposes of this testing document, I'm stepping
52through the internal process of nose so you can see what happens at
53each step.
54
55    >>> plugin = TodoError()
56    >>> result = unittest._TextTestResult(stream=buf,
57    ...                                   descriptions=0, verbosity=2)
58    >>> plugin.prepareTestResult(result)
59
60Now run the test. TODO is printed.
61
62    >>> case(result) # doctest: +ELLIPSIS
63    runTest (....TestTodo) ... TODO: I need to test something
64
65Errors and failures are empty, but todo has our test:
66
67    >>> result.errors
68    []
69    >>> result.failures
70    []
71    >>> result.todo # doctest: +ELLIPSIS
72    [(<....TestTodo testMethod=runTest>, '...Todo: I need to test something\\n')]
73    >>> result.printErrors() # doctest: +ELLIPSIS
74    <BLANKLINE>
75    ======================================================================
76    TODO: runTest (....TestTodo)
77    ----------------------------------------------------------------------
78    Traceback (most recent call last):
79    ...
80    Todo: I need to test something
81    <BLANKLINE>
82
83Since we defined a Todo as a failure, the run was not successful.
84
85    >>> result.wasSuccessful()
86    False
87"""
88
89from new import instancemethod
90from nose.plugins.base import Plugin
91from nose.result import TextTestResult
92from nose.util import isclass
93
94class MetaErrorClass(type):
95    """Metaclass for ErrorClassPlugins that allows error classes to be
96    set up in a declarative manner.
97    """
98    def __init__(self, name, bases, attr):
99        errorClasses = []
100        for name, detail in attr.items():
101            if isinstance(detail, ErrorClass):
102                attr.pop(name)
103                for cls in detail:
104                    errorClasses.append(
105                        (cls, (name, detail.label, detail.isfailure)))
106        super(MetaErrorClass, self).__init__(name, bases, attr)
107        self.errorClasses = tuple(errorClasses)
108
109
110class ErrorClass(object):
111    def __init__(self, *errorClasses, **kw):
112        self.errorClasses = errorClasses
113        try:
114            for key in ('label', 'isfailure'):
115                setattr(self, key, kw.pop(key))
116        except KeyError:
117            raise TypeError("%r is a required named argument for ErrorClass"
118                            % key)
119
120    def __iter__(self):
121        return iter(self.errorClasses)
122
123
124class ErrorClassPlugin(Plugin):
125    """
126    Base class for ErrorClass plugins. Subclass this class and declare the
127    exceptions that you wish to handle as attributes of the subclass.
128    """
129    __metaclass__ = MetaErrorClass
130    score = 1000
131    errorClasses = ()
132
133    def addError(self, test, err):
134        err_cls, a, b = err
135        if not isclass(err_cls):
136            return
137        classes = [e[0] for e in self.errorClasses]
138        if filter(lambda c: issubclass(err_cls, c), classes):
139            return True
140
141    def prepareTestResult(self, result):
142        if not hasattr(result, 'errorClasses'):
143            self.patchResult(result)
144        for cls, (storage_attr, label, isfail) in self.errorClasses:
145            if cls not in result.errorClasses:
146                storage = getattr(result, storage_attr, [])
147                setattr(result, storage_attr, storage)
148                result.errorClasses[cls] = (storage, label, isfail)
149
150    def patchResult(self, result):
151        result._orig_addError, result.addError = \
152            result.addError, add_error_patch(result)
153        result._orig_wasSuccessful, result.wasSuccessful = \
154            result.wasSuccessful, wassuccessful_patch(result)
155        if hasattr(result, 'printErrors'):
156            result._orig_printErrors, result.printErrors = \
157                result.printErrors, print_errors_patch(result)
158        result.errorClasses = {}
159
160
161def add_error_patch(result):
162    """Create a new addError method to patch into a result instance
163    that recognizes the errorClasses attribute and deals with
164    errorclasses correctly.
165    """
166    return instancemethod(
167        TextTestResult.addError.im_func, result, result.__class__)
168
169
170def print_errors_patch(result):
171    """Create a new printErrors method that prints errorClasses items
172    as well.
173    """
174    return instancemethod(
175        TextTestResult.printErrors.im_func, result, result.__class__)
176
177
178def wassuccessful_patch(result):
179    """Create a new wasSuccessful method that checks errorClasses for
180    exceptions that were put into other slots than error or failure
181    but that still count as not success.
182    """
183    return instancemethod(
184        TextTestResult.wasSuccessful.im_func, result, result.__class__)
185
186   
187if __name__ == '__main__':
188    import doctest
189    doctest.testmod()
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。