Skip to content

capsys.disabled for stdin #2189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ikrase opened this issue Jan 12, 2017 · 14 comments
Open

capsys.disabled for stdin #2189

ikrase opened this issue Jan 12, 2017 · 14 comments
Labels
plugin: capture related to the capture builtin plugin

Comments

@ikrase
Copy link

ikrase commented Jan 12, 2017

I'm using pytest 3.0.4

Currently, using capsys.disabled prevents the capturing on stdout. But it does not do anything for stdin, which is also important for my use. (specifically, I want to be able to use input().)

Adding the -s command line option works, but I'd like to do it programmatically in a test. Ideally, you'd get something like this:

    def test_capfd_thing(capfd):
        print("this won't be visible")
        with capsys.disabled():
            print("this will be visible.")
            s = input("Type the word potato:")
            print("you said: "+ s)
        assert 'potato' in s

I'm not sure whether only disabling the capturing of stdout is expected behavior (in which case I would like a way to do what I'm doing above) or a bug.

@RonnyPfannschmidt
Copy link
Member

Input directly kills test automation, why do you want that in automated tests?

@The-Compiler
Copy link
Member

FWIW I had this kind of scenario before (before I switched to pytest):

I wrote tests in Python for an embedded display drawing firmware (written in C). Many tests were automated (communicating with the display over serial port, checking things it gets back, etc.).

However, there is no way I could check if the actual drawing is correct automatically - so I had a test drawing a picture and asked the tester (i.e. myself) if it looks like a reference picture. That test was disabled by default and enabled with a commandline argument, but from time to time it was very valuable.

@RonnyPfannschmidt
Copy link
Member

@The-Compiler but those manual tests dont need to be part of a automated testsuite, this could be just a script

@The-Compiler
Copy link
Member

Sure, the automated tests also could simply be a script. Using a test runner instead provides a lot of convenience, just like with automated tests.

@nicoddemus nicoddemus added the plugin: capture related to the capture builtin plugin label Jan 12, 2017
@nicoddemus
Copy link
Member

Regardless of the use case, seems reasonable to me for capsys.disabled() to behave like --capture=no on the command line, if possible.

Looking at the code it seems that this is what the in_=True parameter was supposed to do, so it warrants some investigation.

@ikrase
Copy link
Author

ikrase commented Jan 12, 2017

I'm basically doing the same thing as @The-Compiler actually.

I'm glad to hear that I'm not crazy WRT capsys.disabled versus --capture=no in any case.

@dusktreader
Copy link

I was hoping this would work so I could make a fixture for dropping into ipdb within tests. However, I still get this output when I do ipdb.set_trace():

https://gist.github.com/dusktreader/ee17bcc1cf282093556cabc636e4ef00

Still looking for a viable workaround for debugging with ipdb

@bilderbuchi
Copy link
Contributor

You're not crazy, I had a related problem in the past (also hardware testing) - there is a way to temporarily suspend capturing: #4210 (comment)

@dusktreader
Copy link

@bilderbuchi Thanks so much for pointing that stuff out! I was able to build my debugger() fixture using that knowledge. Still can't get it to jump into the calling code frame, but other than that it works great:

@pytest.fixture
def debugger(pytestconfig):
    """
    Provides a test fixture that starts an interactive debugger wherever the
    ``debugger()`` fixture is invoked
    """

    def _helper():
        capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
        capmanager.suspend_global_capture(in_=True)
        try:
            # Tried to use inspect.currentframe().f_back to set the calling
            # frame automatically, but it just wouldn't work
            print()
            print()
            print(enboxify(dedent(
                """
                Type 'up' to jump into the test context
                Type 'continue' to resume the test
                Type 'exit' (or ctrl-d) to abort the test
                """
            )))
            import ipdb
            ipdb.set_trace()
        finally:
            capmanager.resume_global_capture()

    return _helper

@blueyed
Copy link
Contributor

blueyed commented Dec 22, 2018

@dusktreader
Why not just use the normal --pdb / pdb.set_trace()?
Check out https://github.com/fschulze/pytest-pdb (which has goto test) and https://github.com/antocuni/pdb also.

@dusktreader
Copy link

@blueyed Because I prefer ipython as a repl

@blueyed
Copy link
Contributor

blueyed commented Jan 3, 2019

@dusktreader
Then try --pdbcls=IPython.terminal.debugger:TerminalPdb maybe.

@mungayree
Copy link

Surfacing this very frequently esp. when we tend to use execute() API of fabfile.py in pytest to run some test case. Any workarounds. I tried with capsys.disabled(), but surface the same.
pytest test_pytest_a.py -s works fine, but with it causing exceptions.

I am using fabric 1.14.1 and pytest 3.7.2

    #test_pytest_a.py
    import pytest
    from fabfile import *
    def test_atest():
        execute(runcmd, hosts=['[email protected]:22'])


   #fabfile.py
   from fabric.api import *
   env.passwords = { '[email protected]:22' : 'xyz' }
   @task
   def runcmd(cmd='date'):
         run(cmd, shell=False)

logs of this show -

  ../py2venv/lib/python2.7/site-packages/fabric/thread_handling.py:12: in wrapper
       callable(*args, **kwargs)
 ../py2venv/lib/python2.7/site-packages/fabric/io.py:227: in input_loop
    r, w, x = select([sys.stdin], [], [], 0.0)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.capture.DontReadFromInput object at 0x7fd9a463b750>

    def fileno(self):
>       raise UnsupportedOperation("redirected stdin is pseudofile, " "has no fileno()")
E       UnsupportedOperation: redirected stdin is pseudofile, has no fileno()

../py2venv/lib/python2.7/site-packages/_pytest/capture.py:599: UnsupportedOperation

@jjaraalm
Copy link

This would be very useful to have for using pytest as a runner for interactive tests. Yes, it could be done with scripts, but pytest brings a whole host of features that are helpful.

This should be fairly simple, can't we just propagate the in_ option?

with capsys.disabled(in_=True):
    input('...')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: capture related to the capture builtin plugin
Projects
None yet
Development

No branches or pull requests

9 participants