| 1 | """ |
|---|
| 2 | This plugin captures stdout during test execution. If the test fails |
|---|
| 3 | or raises an error, the captured output will be appended to the error |
|---|
| 4 | or failure output. It is enabled by default but can be disabled with |
|---|
| 5 | the options ``-s`` or ``--nocapture``. |
|---|
| 6 | |
|---|
| 7 | :Options: |
|---|
| 8 | ``--nocapture`` |
|---|
| 9 | Don't capture stdout (any stdout output will be printed immediately) |
|---|
| 10 | |
|---|
| 11 | """ |
|---|
| 12 | import logging |
|---|
| 13 | import os |
|---|
| 14 | import sys |
|---|
| 15 | from nose.plugins.base import Plugin |
|---|
| 16 | from nose.util import ln |
|---|
| 17 | from StringIO import StringIO |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | log = logging.getLogger(__name__) |
|---|
| 21 | |
|---|
| 22 | class Capture(Plugin): |
|---|
| 23 | """ |
|---|
| 24 | Output capture plugin. Enabled by default. Disable with ``-s`` or |
|---|
| 25 | ``--nocapture``. This plugin captures stdout during test execution, |
|---|
| 26 | appending any output captured to the error or failure output, |
|---|
| 27 | should the test fail or raise an error. |
|---|
| 28 | """ |
|---|
| 29 | enabled = True |
|---|
| 30 | env_opt = 'NOSE_NOCAPTURE' |
|---|
| 31 | name = 'capture' |
|---|
| 32 | score = 500 |
|---|
| 33 | |
|---|
| 34 | def __init__(self): |
|---|
| 35 | self.stdout = [] |
|---|
| 36 | self._buf = None |
|---|
| 37 | |
|---|
| 38 | def options(self, parser, env): |
|---|
| 39 | """Register commandline options |
|---|
| 40 | """ |
|---|
| 41 | parser.add_option( |
|---|
| 42 | "-s", "--nocapture", action="store_false", |
|---|
| 43 | default=not env.get(self.env_opt), dest="capture", |
|---|
| 44 | help="Don't capture stdout (any stdout output " |
|---|
| 45 | "will be printed immediately) [NOSE_NOCAPTURE]") |
|---|
| 46 | |
|---|
| 47 | def configure(self, options, conf): |
|---|
| 48 | """Configure plugin. Plugin is enabled by default. |
|---|
| 49 | """ |
|---|
| 50 | self.conf = conf |
|---|
| 51 | if not options.capture: |
|---|
| 52 | self.enabled = False |
|---|
| 53 | |
|---|
| 54 | def afterTest(self, test): |
|---|
| 55 | """Clear capture buffer. |
|---|
| 56 | """ |
|---|
| 57 | self.end() |
|---|
| 58 | self._buf = None |
|---|
| 59 | |
|---|
| 60 | def begin(self): |
|---|
| 61 | """Replace sys.stdout with capture buffer. |
|---|
| 62 | """ |
|---|
| 63 | self.start() # get an early handle on sys.stdout |
|---|
| 64 | |
|---|
| 65 | def beforeTest(self, test): |
|---|
| 66 | """Flush capture buffer. |
|---|
| 67 | """ |
|---|
| 68 | self.start() |
|---|
| 69 | |
|---|
| 70 | def formatError(self, test, err): |
|---|
| 71 | """Add captured output to error report. |
|---|
| 72 | """ |
|---|
| 73 | test.capturedOutput = output = self.buffer |
|---|
| 74 | self._buf = None |
|---|
| 75 | if not output: |
|---|
| 76 | # Don't return None as that will prevent other |
|---|
| 77 | # formatters from formatting and remove earlier formatters |
|---|
| 78 | # formats, instead return the err we got |
|---|
| 79 | return err |
|---|
| 80 | ec, ev, tb = err |
|---|
| 81 | return (ec, self.addCaptureToErr(ev, output), tb) |
|---|
| 82 | |
|---|
| 83 | def formatFailure(self, test, err): |
|---|
| 84 | """Add captured output to failure report. |
|---|
| 85 | """ |
|---|
| 86 | return self.formatError(test, err) |
|---|
| 87 | |
|---|
| 88 | def addCaptureToErr(self, ev, output): |
|---|
| 89 | return '\n'.join([str(ev) , ln('>> begin captured stdout <<'), |
|---|
| 90 | output, ln('>> end captured stdout <<')]) |
|---|
| 91 | |
|---|
| 92 | def start(self): |
|---|
| 93 | self.stdout.append(sys.stdout) |
|---|
| 94 | self._buf = StringIO() |
|---|
| 95 | sys.stdout = self._buf |
|---|
| 96 | |
|---|
| 97 | def end(self): |
|---|
| 98 | if self.stdout: |
|---|
| 99 | sys.stdout = self.stdout.pop() |
|---|
| 100 | |
|---|
| 101 | def finalize(self, result): |
|---|
| 102 | """Restore stdout. |
|---|
| 103 | """ |
|---|
| 104 | while self.stdout: |
|---|
| 105 | self.end() |
|---|
| 106 | |
|---|
| 107 | def _get_buffer(self): |
|---|
| 108 | if self._buf is not None: |
|---|
| 109 | return self._buf.getvalue() |
|---|
| 110 | |
|---|
| 111 | buffer = property(_get_buffer, None, None, |
|---|
| 112 | """Captured stdout output.""") |
|---|