--- /usr/lib/python2.5/unittest.py	2006-10-29 12:44:30.000000000 +0100
+++ unittest.py	2006-11-02 22:57:52.458441021 +0100
@@ -208,14 +208,6 @@
             raise ValueError, "no such test method in %s: %s" % \
                   (self.__class__, methodName)
 
-    def setUp(self):
-        "Hook method for setting up the test fixture before exercising it."
-        pass
-
-    def tearDown(self):
-        "Hook method for deconstructing the test fixture after testing it."
-        pass
-
     def countTestCases(self):
         return 1
 
@@ -248,7 +240,7 @@
         testMethod = getattr(self, self._testMethodName)
         try:
             try:
-                self.setUp()
+                self._get_setup()()
             except KeyboardInterrupt:
                 raise
             except:
@@ -267,7 +259,7 @@
                 result.addError(self, self._exc_info())
 
             try:
-                self.tearDown()
+                self._get_teardown()()
             except KeyboardInterrupt:
                 raise
             except:
@@ -296,6 +288,29 @@
             return (exctype, excvalue, tb)
         return (exctype, excvalue, tb)
 
+    def _get_setup(self):
+        """Return this class's setup method."""
+        return self._get_method("setUp", "_unittest_setup")
+
+    def _get_teardown(self):
+        """Return this class's teardown method."""
+        return self._get_method("tearDown", "_unittest_teardown")
+
+    def _get_method(self, methodname, attribute):
+        """Return this class's method called "methodname", or a method with
+        the attribute "attribute". If neither exists, returns a noop method."""
+        try:
+            return getattr(self, methodname)
+        except AttributeError, e:
+            for attrname in self.__class__.__dict__:
+                attr = self.__class__.__dict__[attrname]
+                try:
+                    if getattr(attr, attribute):
+                        return lambda *args, **kwargs: attr(self, *args, **kwargs)
+                except AttributeError, e:
+                    pass
+            return lambda: None
+
     def fail(self, msg=None):
         """Fail immediately, with the given message."""
         raise self.failureException, msg
@@ -560,7 +575,10 @@
         """Return a sorted sequence of method names found within testCaseClass
         """
         def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix):
-            return attrname.startswith(prefix) and callable(getattr(testCaseClass, attrname))
+            attr = getattr(testCaseClass, attrname)
+            if attrname.startswith(prefix) and callable(attr):
+                return True
+            return hasattr(attr, "_unittest_test")
         testFnNames = filter(isTestMethod, dir(testCaseClass))
         for baseclass in testCaseClass.__bases__:
             for testFnName in self.getTestCaseNames(baseclass):
@@ -718,6 +736,26 @@
 
 
 ##############################################################################
+# Decorators
+##############################################################################
+
+def test(method):
+    """A decorator for test cases."""
+    method._unittest_test = True
+    return method
+
+def setup(method):
+    """A decorator for test setup methods."""
+    method._unittest_setup = True
+    return method
+
+def teardown(method):
+    """A decorator for test teardown methods."""
+    method._unittest_teardown = True
+    return method
+
+
+##############################################################################
 # Facilities for running tests from the command line
 ##############################################################################
 
