[pytest-dev] Trouble accessing captured stderr

Nikolaus Rath Nikolaus at rath.org
Tue Jun 4 07:03:27 CEST 2013


Hello,

I have a problem accessing the captured stderr. I have set up the
following test:

$ ls mytest/
conftest.py  test_one.py

$ cat mytest/conftest.py
import pytest

@pytest.fixture(autouse=True)
def add_stderr_check(request, capsys):
    def check_stderr():
        stderr = capsys.readouterr()[1]
        if 'exception' in stderr.lower():
            raise AssertionError('Suspicious output to stderr')
    request.addfinalizer(check_stderr)

$ cat mytest/test_one.py
import threading

def test_thread():
    def fail():
        raise RuntimeError('Do not call me!')
    t = threading.Thread(target=fail)
    t.start()
    t.join()

def test_del():
    d = ImmortalObject()
    assert d.value == 2
    del d

class ImmortalObject:
    def __init__(self):
        self.value = 2

    def __del__(self):
        raise RuntimeError('You cannot kill me!')


As long as I call the tests individually, they behave exactly as expected:


> $ py.test mytest/ -v -k test_thread
> =========================================== test session starts ============================================
> platform linux -- Python 3.3.2 -- pytest-2.3.5 -- /usr/bin/python3.3
> collected 2 items 
> 
> mytest/test_one.py:3: test_thread PASSED
> mytest/test_one.py:3: test_thread ERROR
> 
> ================================================== ERRORS ==================================================
> _____________________________________ ERROR at teardown of test_thread _____________________________________
> 
>     def check_stderr():
>         stderr = capsys.readouterr()[1]
>         if 'exception' in stderr.lower():
>>           raise AssertionError('Suspicious output to stderr')
> E           AssertionError: Suspicious output to stderr
> 
> mytest/conftest.py:8: AssertionError
> --------------------------------------------- Captured stderr ----------------------------------------------
> Exception in thread Thread-1:
> Traceback (most recent call last):
>   File "/usr/lib/python3.3/threading.py", line 637, in _bootstrap_inner
>     self.run()
>   File "/usr/lib/python3.3/threading.py", line 594, in run
>     self._target(*self._args, **self._kwargs)
>   File "/home/nikratio/in-progress/s3ql/mytest/test_one.py", line 5, in fail
>     raise RuntimeError('Do not call me!')
> RuntimeError: Do not call me!
> 
> ================================== 1 tests deselected by '-ktest_thread' ===================================
> ============================= 1 passed, 1 deselected, 1 error in 0.01 seconds ==============================

and:

> [1] nikratio at vostro:~/in-progress/s3ql$ py.test mytest/ -v -k test_del
> =========================================== test session starts ============================================
> platform linux -- Python 3.3.2 -- pytest-2.3.5 -- /usr/bin/python3.3
> collected 2 items 
> 
> mytest/test_one.py:10: test_del PASSED
> mytest/test_one.py:10: test_del ERROR
> 
> ================================================== ERRORS ==================================================
> ______________________________________ ERROR at teardown of test_del _______________________________________
> 
>     def check_stderr():
>         stderr = capsys.readouterr()[1]
>         if 'exception' in stderr.lower():
>>           raise AssertionError('Suspicious output to stderr')
> E           AssertionError: Suspicious output to stderr
> 
> mytest/conftest.py:8: AssertionError
> --------------------------------------------- Captured stderr ----------------------------------------------
> Exception RuntimeError: RuntimeError('You cannot kill me!',) in <bound method ImmortalObject.__del__ of <test_one.ImmortalObject object at 0x7f9fb945fb90>> ignored
> ==================================== 1 tests deselected by '-ktest_del' ====================================
> ============================= 1 passed, 1 deselected, 1 error in 0.01 seconds ==============================

However, if I attempt to run both tests, I get a very long and (to me)
confusing error:

> $ py.test mytest/ -v 
> =========================================== test session starts ============================================
> platform linux -- Python 3.3.2 -- pytest-2.3.5 -- /usr/bin/python3.3
> collected 2 items 
> 
> mytest/test_one.py:3: test_thread PASSED
> mytest/test_one.py:3: test_thread ERROR
> mytest/test_one.py:10: test_del FAILED
> 
> ================================================== ERRORS ==================================================
> _____________________________________ ERROR at teardown of test_thread _____________________________________
> 
>     def check_stderr():
>         stderr = capsys.readouterr()[1]
>         if 'exception' in stderr.lower():
>>           raise AssertionError('Suspicious output to stderr')
> E           AssertionError: Suspicious output to stderr
> 
> mytest/conftest.py:8: AssertionError
> --------------------------------------------- Captured stderr ----------------------------------------------
> Exception in thread Thread-1:
> Traceback (most recent call last):
>   File "/usr/lib/python3.3/threading.py", line 637, in _bootstrap_inner
>     self.run()
>   File "/usr/lib/python3.3/threading.py", line 594, in run
>     self._target(*self._args, **self._kwargs)
>   File "/home/nikratio/in-progress/s3ql/mytest/test_one.py", line 5, in fail
>     raise RuntimeError('Do not call me!')
> RuntimeError: Do not call me!
> 
> ================================================= FAILURES =================================================
> _________________________________________________ test_del _________________________________________________
> 
> self = <CallInfo when='call' exception: 'CaptureFixture' object has no attribute 'capture'>
> func = <function call_runtest_hook.<locals>.<lambda> at 0x7f413dfc5950>, when = 'call'
> 
>     def __init__(self, func, when):
>         #: context of invocation: one of "setup", "call",
>         #: "teardown", "memocollect"
>         self.when = when
>         self.start = time()
>         try:
>             try:
>>               self.result = func()
> 
> ../../.local/lib/python3.3/site-packages/_pytest/runner.py:129: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
>>   return CallInfo(lambda: ihook(item=item, **kwds), when=when)
> 
> ../../.local/lib/python3.3/site-packages/_pytest/runner.py:116: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> kwargs = {'item': <Function 'test_del'>}
> plugins = [<Session 's3ql'>, <_pytest.core.PluginManager object at 0x7f413eb6dfd0>, <module '_pytest.config' from '/home/nikrati...in.py'>, <module '_pytest.terminal' from '/home/nikratio/.local/lib/python3.3/site-packages/_pytest/terminal.py'>, ...]
> 
>     def call_matching_hooks(**kwargs):
>         plugins = self.config._getmatchingplugins(self.fspath)
>>       return hookmethod.pcall(plugins, **kwargs)
> 
> ../../.local/lib/python3.3/site-packages/_pytest/main.py:159: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <HookCaller 'pytest_runtest_call'>
> plugins = [<Session 's3ql'>, <_pytest.core.PluginManager object at 0x7f413eb6dfd0>, <module '_pytest.config' from '/home/nikrati...in.py'>, <module '_pytest.terminal' from '/home/nikratio/.local/lib/python3.3/site-packages/_pytest/terminal.py'>, ...]
> kwargs = {'item': <Function 'test_del'>}
> methods = [<function pytest_runtest_call at 0x7f413e9314d0>, <function pdbitem at 0x7f413e9544d0>, <bound method CaptureManager.pytest_runtest_call of <_pytest.capture.CaptureManager object at 0x7f413eb77710>>]
> 
>     def pcall(self, plugins, **kwargs):
>         methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
>>       return self._docall(methods, kwargs)
> 
> ../../.local/lib/python3.3/site-packages/_pytest/core.py:445: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <HookCaller 'pytest_runtest_call'>
> methods = [<function pytest_runtest_call at 0x7f413e9314d0>, <function pdbitem at 0x7f413e9544d0>, <bound method CaptureManager.pytest_runtest_call of <_pytest.capture.CaptureManager object at 0x7f413eb77710>>]
> kwargs = {'item': <Function 'test_del'>}
> 
>     def _docall(self, methods, kwargs):
>         self.trace(self.name, kwargs)
>         self.trace.root.indent += 1
>         mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
>         try:
>>           res = mc.execute()
> 
> ../../.local/lib/python3.3/site-packages/_pytest/core.py:452: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <MultiCall 0 results, 2 meths, kwargs={'item': <Function 'test_del'>}>
> 
>     def execute(self):
>         while self.methods:
>             method = self.methods.pop()
>             kwargs = self.getkwargs(method)
>>           res = method(**kwargs)
> 
> ../../.local/lib/python3.3/site-packages/_pytest/core.py:370: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <_pytest.capture.CaptureManager object at 0x7f413eb77710>, item = <Function 'test_del'>
> 
>     @pytest.mark.tryfirst
>     def pytest_runtest_call(self, item):
>         self.resumecapture_item(item)
>>       self.activate_funcargs(item)
> 
> ../../.local/lib/python3.3/site-packages/_pytest/capture.py:158: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <_pytest.capture.CaptureManager object at 0x7f413eb77710>, pyfuncitem = <Function 'test_del'>
> 
>     def activate_funcargs(self, pyfuncitem):
>         funcargs = getattr(pyfuncitem, "funcargs", None)
>         if funcargs is not None:
>             for name, capfuncarg in funcargs.items():
>                 if name in ('capsys', 'capfd'):
>                     assert not hasattr(self, '_capturing_funcarg')
>                     self._capturing_funcarg = capfuncarg
>>                   capfuncarg._start()
> 
> ../../.local/lib/python3.3/site-packages/_pytest/capture.py:128: 
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
> 
> self = <_pytest.capture.CaptureFixture object at 0x7f413ea47f10>
> 
>     def _start(self):
>>       self.capture.startall()
> E       AttributeError: 'CaptureFixture' object has no attribute 'capture'
> 
> ../../.local/lib/python3.3/site-packages/_pytest/capture.py:209: AttributeError
> =============================== 1 failed, 1 passed, 1 error in 0.03 seconds ================================


Can someone explain what's happening here?


Best,

   -Nikolaus

-- 
 »Time flies like an arrow, fruit flies like a Banana.«

  PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6  02CF A9AD B7F8 AE4E 425C


More information about the Pytest-dev mailing list