Description
The title is a bit obscure, but I think it is better explained with an example. Suppose that the following conditions are met:
- a C++ extension which uses
std::cout
inside itsinitxxx(void)
function - you import this extension in the conftest
- you use
--capture=fd
(the default) - you use
with capsys.disabled():
(either directly or indirectly by using--pdb
)
In that case, calls to stdio.write()
are not flushed automatically. The following gist includes a working example:
https://gist.github.com/antocuni/b444c2c8b37821a95f45bee42e78d3cf
To reproduce, simply run:
$ python setup.py build_ext -i
$ py.test test_noflush.py
You should be able to see the numbers 0..9 to be printed at regular intervals. However, you see them only when stdout is flushed.
Another way to see the problem is to use --pdb
; using the provided gist, do the following:
py.test test_noflush.py -k test_enter_pdb --pdb
...
(Pdb) sys.stdout.write('hello\n'); time.sleep(1)
hello
Normally, you would expect to see hello
while sleeping; however, because of the bug you see hello
only AFTER the sleep (this happens because pdb
uses readline
which flushes stdout before displaying the prompt).
If you do either one of the following things, the bug disappear:
- comment out
import dummy
insideconftest.py
OR - comment out the call to
std::cout
insidedummy.cpp
OR - uncomment the call to
sys.stdout.flush()
intest_noflush.py
Additional info:
pip list
of the virtual environment you are using
Package Version
-------------- -------
atomicwrites 1.3.0
attrs 19.1.0
funcsigs 1.0.2
more-itertools 5.0.0
pathlib2 2.3.3
pip 19.0.3
pluggy 0.9.0
py 1.8.0
pytest 4.4.1
scandir 1.10.0
setuptools 41.0.0
six 1.12.0
wheel 0.33.1
- pytest and operating system versions:
$ pytest --version
This is pytest version 4.4.1, imported from /tmp/yyy/local/lib/python2.7/site-packages/pytest.pyc
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.2 LTS
Release: 18.04
Codename: bionic
Activity
antocuni commentedon Apr 16, 2019
Forgot to mention the python version:
asottile commentedon Apr 16, 2019
capsys
works at the python stream level (sys.stdout
/sys.stderr
) -- you're looking forcapfd
See https://docs.pytest.org/en/latest/capture.html
antocuni commentedon Apr 16, 2019
You are correct. However, the bug is still there even if I use
capfd
. I updated my gist accordinglyasottile commentedon Apr 16, 2019
ah I see, I misread your issue and assumed you were trying to capture the c++ output.
I think maybe there's a misunderstanding here about how streams work in python, in particular the standard streams which are buffered (for performance)
Let's factor out the c++ module entirely from the situation:
Now try the following commands:
Adding a
flush()
makes all three invocations consistentantocuni commentedon Apr 16, 2019
I know about buffering, but I don't think that's not the point of the issue. Note that the issue happens only if both these three conditions are met:
--capture=fd
If the problem were the buffering of
sys.stdout
, you would get the same behavior even without 1 or 2 or 3. I don't think there is an "easy" explanation for this behavior (or at least, I couldn't find one after spending 1 day on it). I suspect it is a bug ofcapfd
, that's why I reported the issue here.antocuni commentedon Apr 16, 2019
Some more notes:
maybe I didn't write it very clearly, but the problem arises only if you
import dummy
is in the conftest; if you import it later (e.g. intest_noflush.py
) it works correctly. I suppose that the difference is that in the first case, the C++ extension prints before capfd is fully initialised, or something like that.I just tried and it doesn't need to be C++; a normal C extension using
printf()
exhibits the same behaviorasottile commentedon Apr 16, 2019
Here's a reproduction not involving pytest:
antocuni commentedon Apr 16, 2019
Ok, thanks for the example; I can see that either
import dummy
orsys.stdout.write
is enough to trigger the behavior, even if I don't understand why (if you comment out both, you see the subsequent writes immediately).However, my point is that if pytest is able to handle correctly the
import dummy
at the beginning of a test file, it should also be able to handle it correctly inside aconftest.py
.The example I attached is minimal and contrived, however I have see at least one real-world case in which things started to break and/or to function differently because I moved an import from
test_*.py
toconftest.py
.blueyed commentedon Apr 18, 2019
Does using
pytest -s
make a difference?(There is special wrapping of capturing around conftests IIRC, which might even be done unconditionally IIRC.)
blueyed commentedon Apr 18, 2019
Also try it with the features branch of pytest - there might be fixes in this regard already.
blueyed commentedon Apr 18, 2019
Maybe a
flush
is needed within pytest's capture machinery before moving fds around, or something similar?make sure to flush stdout/stderr when calling readline()/raw_input().…
21 remaining items