Commits

jpellerin  committed 4537746

Improved compatibility with 2.7 builtin skip

  • Participants
  • Parent commits 0ba5d44

Comments (0)

Files changed (5)

File nose/plugins/errorclass.py

 the result. This is an internal format and subject to change; you
 should always use the declarative syntax for attaching ErrorClasses to
 an ErrorClass plugin.
-    
+
     >>> TodoError.errorClasses # doctest: +ELLIPSIS
     ((<class ...Todo...>, ('todo', 'TODO', True)),)
 
     ...     from unittest.runner import _WritelnDecorator
     ... except ImportError:
     ...     from unittest import _WritelnDecorator
-    ... 
+    ...
     >>> buf = _WritelnDecorator(sys.stdout)
 
 Now define a test case that raises a Todo.
                 result.errorClasses[cls] = (storage, label, isfail)
 
     def patchResult(self, result):
+        result.printLabel = print_label_patch(result)
         result._orig_addError, result.addError = \
             result.addError, add_error_patch(result)
         result._orig_wasSuccessful, result.wasSuccessful = \
         if hasattr(result, 'printErrors'):
             result._orig_printErrors, result.printErrors = \
                 result.printErrors, print_errors_patch(result)
+        if hasattr(result, 'addSkip'):
+            result._orig_addSkip, result.addSkip = \
+                result.addSkip, add_skip_patch(result)
         result.errorClasses = {}
 
 
         TextTestResult.printErrors.im_func, result, result.__class__)
 
 
+def print_label_patch(result):
+    """Create a new printLabel method that prints errorClasses items
+    as well.
+    """
+    return instancemethod(
+        TextTestResult.printLabel.im_func, result, result.__class__)
+
+
 def wassuccessful_patch(result):
     """Create a new wasSuccessful method that checks errorClasses for
     exceptions that were put into other slots than error or failure
     return instancemethod(
         TextTestResult.wasSuccessful.im_func, result, result.__class__)
 
-    
+
+def add_skip_patch(result):
+    """Create a new addSkip method to patch into a result instance
+    that delegates to addError.
+    """
+    return instancemethod(
+        TextTestResult.addSkip.im_func, result, result.__class__)
+
+
 if __name__ == '__main__':
     import doctest
     doctest.testmod()

File nose/plugins/skip.py

 from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin
 
 
-class SkipTest(Exception):
-    """Raise this exception to mark a test as skipped.
-    """
+try:
+    # 2.7
+    from unittest.case import SkipTest
+except ImportError:
+    # 2.6 and below
+    class SkipTest(Exception):
+        """Raise this exception to mark a test as skipped.
+        """
     pass
 
 

File nose/proxy.py

     the wrapped test case) as each result call is made. Finally, the
     real result method is called, also with the nose.case.Test
     instance as the test parameter.
-    
-    """    
+
+    """
     def __init__(self, result, test, config=None):
         if config is None:
             config = Config()
         # .test's .test. or my .test.test's .case
 
         case = getattr(self.test, 'test', None)
-        assert (test is self.test 
-                or test is case 
-                or test is getattr(case, '_nose_case', None)), ( 
-                "ResultProxy for %r (%s) was called with test %r (%s)" 
+        assert (test is self.test
+                or test is case
+                or test is getattr(case, '_nose_case', None)), (
+                "ResultProxy for %r (%s) was called with test %r (%s)"
                 % (self.test, id(self.test), test, id(test)))
-    
+
     def afterTest(self, test):
         self.assertMyTest(test)
         self.plugins.afterTest(self.test)
         self.result.addFailure(self.test, err)
         if self.config.stopOnError:
             self.shouldStop = True
-    
+
+    def addSkip(self, test, reason):
+        # 2.7 compat shim
+        from nose.plugins.skip import SkipTest
+        self.assertMyTest(test)
+        plugins = self.plugins
+        plugins.addError(self.test, (SkipTest, reason, None))
+        self.result.addSkip(self.test, reason)
+
     def addSuccess(self, test):
         self.assertMyTest(test)
         self.plugins.addSuccess(self.test)
         self.assertMyTest(test)
         self.plugins.startTest(self.test)
         self.result.startTest(self.test)
-    
+
     def stop(self):
         self.result.stop()
-    
+
     def stopTest(self, test):
         self.assertMyTest(test)
         self.plugins.stopTest(self.test)

File nose/result.py

     """Text test result that extends unittest's default test result
     support for a configurable set of errorClasses (eg, Skip,
     Deprecated, TODO) that extend the errors/failures/success triad.
-    """    
+    """
     def __init__(self, stream, descriptions, verbosity, config=None,
-                 errorClasses=None):        
+                 errorClasses=None):
         if errorClasses is None:
             errorClasses = {}
         self.errorClasses = errorClasses
         if config is None:
-            config = Config()       
+            config = Config()
         self.config = config
         _TextTestResult.__init__(self, stream, descriptions, verbosity)
-                
+
+    def addSkip(self, test, reason):
+        # 2.7 skip compat
+        from nose.plugins.skip import SkipTest
+        if SkipTest in self.errorClasses:
+            storage, label, isfail = self.errorClasses[SkipTest]
+            storage.append((test, reason))
+            self.printLabel(label, (SkipTest, reason, None))
+
     def addError(self, test, err):
         """Overrides normal addError to add support for
         errorClasses. If the exception is a registered class, the
         error will be added to the list for that class, not errors.
         """
-        stream = getattr(self, 'stream', None)
         ec, ev, tb = err
         try:
             exc_info = self._exc_info_to_string(err, test)
             # 2.3 compat
             exc_info = self._exc_info_to_string(err)
         for cls, (storage, label, isfail) in self.errorClasses.items():
+            #if 'Skip' in cls.__name__ or 'Skip' in ec.__name__:
+            #    from nose.tools import set_trace
+            #    set_trace()
             if isclass(ec) and issubclass(ec, cls):
                 if isfail:
                     test.passed = False
                 storage.append((test, exc_info))
-                # Might get patched into a streamless result
-                if stream is not None:
-                    if self.showAll:
-                        message = [label]
-                        detail = _exception_detail(err[1])
-                        if detail:
-                            message.append(detail)
-                        stream.writeln(": ".join(message))
-                    elif self.dots:
-                        stream.write(label[:1])
+                self.printLabel(label, err)
                 return
         self.errors.append((test, exc_info))
         test.passed = False
+        self.printLabel('ERROR')
+
+    def printLabel(self, label, err=None):
+        # Might get patched into a streamless result
+        stream = getattr(self, 'stream', None)
         if stream is not None:
             if self.showAll:
-                self.stream.writeln('ERROR')
+                message = [label]
+                if err:
+                    detail = _exception_detail(err[1])
+                    if detail:
+                        message.append(detail)
+                stream.writeln(": ".join(message))
             elif self.dots:
-                stream.write('E')
+                stream.write(label[:1])
 
     def printErrors(self):
         """Overrides to print all errorClasses errors as well.
         taken = float(stop - start)
         run = self.testsRun
         plural = run != 1 and "s" or ""
-        
+
         writeln(self.separator2)
         writeln("Ran %s test%s in %.3fs" % (run, plural, taken))
         writeln()
             self.stream.write('E')
 
     def _exc_info_to_string(self, err, test=None):
+        # 2.7 skip compat
+        from nose.plugins.skip import SkipTest
+        if issubclass(err[0], SkipTest):
+            return str(err[1])
         # 2.3/2.4 -- 2.4 passes test, 2.3 does not
         try:
             return _TextTestResult._exc_info_to_string(self, err, test)
          "from nose.result in a future release. Please update your imports ",
          DeprecationWarning)
     return _ln(*arg, **kw)
-    
 
+

File unit_tests/test_skip_plugin.py

         sk = Skip()
         sk.addOptions
         sk.configure
-        sk.prepareTestResult        
+        sk.prepareTestResult
 
     def test_prepare_patches_result(self):
         stream = _WritelnDecorator(StringIO())
         class NoPatch(unittest.TestResult):
             def __init__(self):
                 self.errorClasses = {}
-                
+
         res = NoPatch()
         sk = Skip()
         sk.prepareTestResult(res)
         assert not hasattr(res, '_orig_addError'), \
                "Skip patched a result class it didn't need to patch"
-        
+
 
     def test_skip_output(self):
         class TC(unittest.TestCase):
         test = TC('test')
         test(res)
         assert not res.errors, "Skip was not caught: %s" % res.errors
-        assert res.skipped            
+        assert res.skipped
 
         res.printErrors()
         out = stream.getvalue()
+        print out
         assert out
         assert out.strip() == "S"
         assert res.wasSuccessful()
         class TC(unittest.TestCase):
             def test(self):
                 raise SkipTest('skip me too')
-        
+
         stream = _WritelnDecorator(StringIO())
         res = _TextTestResult(stream, 0, verbosity=2)
         sk = Skip()
         test = TC('test')
         test(res)
         assert not res.errors, "Skip was not caught: %s" % res.errors
-        assert res.skipped            
+        assert res.skipped
 
         res.printErrors()
         out = stream.getvalue()