Pytest: Ability to disable capture from within a test

Created on 9 Jun 2016  路  13Comments  路  Source: pytest-dev/pytest

(This was discussed #167 but got side-tracked)

Proposal to disable capturing from within a test:

def test_foo(capsys):
    print('captured')
    with capsys.disabled():
         print('directly to stdout, regardless of the "-s" flag')
    print('captured again')

Optionally, provide a marker which does the same job:

@pytest.mark.capture_disabled
def test_foo():
    print('directly to stdout, regardless of the "-s" flag')    
capture proposal

Most helpful comment

Found a solution 馃榿

class Test:

    @pytest.fixture(scope='class')
    def setup(self, pytestconfig):
        capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
        capmanager.suspendcapture()
        print('hello from class')
        capmanager.resumecapture()

    def test_foo(self, setup):
        print('cannot see me')
============================= test session starts =============================
platform win32 -- Python 3.5.0, pytest-3.1.0.dev0, py-1.4.31, pluggy-0.4.0
rootdir: C:\pytest, inifile: tox.ini
plugins: hypothesis-3.5.0
collected 1 items

.tmp\demo-b\test_foo3.py hello from class
.

========================== 1 passed in 0.01 seconds ===========================

Caveats: uses internal pytest objects, so this may break in the future. Other than that, is exactly what pytest does internally so it should work fine.

All 13 comments

@nicoddemus The with syntax is very nice - but only works on a function level and not a class level. Can this be changed?

It works just fine on methods as well. Could you post an example of what you are seeing?

@nicoddemus

python ScopeMismatch: You tried to access the 'function' scoped fixture 'capsys' with a 'class' scoped request object, involved factories lib.macosx-10.6-x86_64-2.7/tardis/tests/integration_tests/test_integration.py:24: def setup(self, request, reference, data_path, capsys)

Ahh sorry I though you meant functions vs methods. 馃槄

Unfortunately no. We plan to have a new scope for fixtures called "invocation" which would allow this kind of thing (#1794), but unfortunately it had to be pulled off at the last minute because it contained a serious flaw on it.

@nicoddemus so how can I do this now? is there a way instead of using -s for this purpose?

I thought on a workaround: copy pytest's implementation, name it capsys_class and change the scope of the fixture to class. But unfortunately it didn't work.

Could you describe in more details why you need that functionality in a class-scoped fixture? We might have other suggestions on how to accomplish it.

We are testing data generated with this function: https://github.com/tardis-sn/tardis/blob/master/tardis/tests/integration_tests/test_integration.py#L77 and want to make sure that we see what happens when it runs (it takes a long time to run).

Oh I see. I can't think of a workaround for that, sorry.

@nicoddemus thanks for looking into this. Well, can I somehow enable -s when I switch on a different command line option within the code?

Found a solution 馃榿

class Test:

    @pytest.fixture(scope='class')
    def setup(self, pytestconfig):
        capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
        capmanager.suspendcapture()
        print('hello from class')
        capmanager.resumecapture()

    def test_foo(self, setup):
        print('cannot see me')
============================= test session starts =============================
platform win32 -- Python 3.5.0, pytest-3.1.0.dev0, py-1.4.31, pluggy-0.4.0
rootdir: C:\pytest, inifile: tox.ini
plugins: hypothesis-3.5.0
collected 1 items

.tmp\demo-b\test_foo3.py hello from class
.

========================== 1 passed in 0.01 seconds ===========================

Caveats: uses internal pytest objects, so this may break in the future. Other than that, is exactly what pytest does internally so it should work fine.

@nicoddemus thanks - that's the one!!

Whoever may stumble on this useful technique, please be aware that in the meantime capmanager.suspendcapture and capmanager.resumecapture were renamed to capmanager.suspend_global_capture and capmanager.resume_global_capture, respectively.

@pytest.fixture(scope='class')
def suspend_capture(pytestconfig):
    class suspend_guard:
        def __init__(self):
            self.capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
        def __enter__(self):
            self.capmanager.suspend_global_capture(in_=True)
            pass
        def __exit__(self, _1, _2, _3):
            self.capmanager.resume_global_capture()

    yield suspend_guard()


def test_input(suspend_capture):
    with suspend_capture:
        input("hello")
Was this page helpful?
0 / 5 - 0 ratings