Covered lines marked as missing after gevent.joinall.
Describe the bug
Lines after a call to gevent.joinall() are reported as not covered even though they get run.
To Reproduce
-
What version of Python are you running?
3.6.5 -
What versions of what packages do you have installed? The output of
pip freezeis very helpful.
coverage==4.5.4
gevent==1.4.0
greenlet==0.4.15
- What code are you running? Give us a specific commit of a specific repo that we can check out.
test_foo.py:
import gevent
import unittest
class TestJoinAll(unittest.TestCase):
def test_joinall(self):
def coroutine():
return 5
greenlet = gevent.spawn(coroutine)
gevent.joinall([greenlet])
result = greenlet.get() # this line runs, but is not covered
assert 4 == result # this line runs, but is not covered
if __name__ == "__main__":
unittest.main()
- What commands did you run?
coverage run --include=test_foo.py --concurrency=gevent -m test_fooThe above command should fail. Change the4to a5to get it to pass.
F
======================================================================
FAIL: test_joinall (__main__.TestJoinAll)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/yury/foo/test_foo.py", line 14, in test_joinall
assert 4 == result # this line runs, but is not covered
AssertionError
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
coverage report -m
This will indicate that two lines are not covered.
Name Stmts Miss Cover Missing
-------------------------------------------
test_foo.py 12 2 83% 13-14
Expected behavior Lines 13 and 14 clearly ran as they caused the test to fail. They should be reported as covered.
Additional context
Changing gevent.joinall([greenlet]) to greenlet.join() fixes the problem.
This might be related to #663. I wrote a unit test and it passed with gevent==1.2.2. In fact, the problem resolves itself if PURE_PYTHON=1 is set causing gevent to not use its C acceleration. Of course, this is not a great workaround.
In case you want it, here is the failing unit test (with gevent==1.4.0):
diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py
index 9d777afe..b3fac048 100644
--- a/tests/test_concurrency.py
+++ b/tests/test_concurrency.py
@@ -272,6 +272,16 @@ class ConcurrencyTest(CoverageTest):
code = SIMPLE.format(QLIMIT=self.QLIMIT)
self.try_some_code(code, "gevent", gevent)
+ def test_gevent_joinall(self):
+ code = """\
+ import gevent
+
+ greenlet = gevent.spawn(lambda: None)
+ gevent.joinall([greenlet])
+ print("covered after joinall")
+ """
+ self.try_some_code(code, "gevent", gevent, "covered after joinall\n")
+
def test_greenlet(self):
GREENLET = """\
from greenlet import greenlet
diff --git a/tox.ini b/tox.ini
index bf636f27..b223f246 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,7 +16,7 @@ deps =
pip==19.1.1
setuptools==41.0.1
# gevent 1.3 causes a failure: https://github.com/nedbat/coveragepy/issues/663
- py{27,35,36}: gevent==1.2.2
+ py{27,35,36}: gevent==1.4.0
py{27,35,36,37,38}: eventlet==0.24.1
py{27,35,36,37,38}: greenlet==0.4.15
It fails with
tests/test_concurrency.py:249: in try_some_code
self.assertEqual(line_counts(data)['try_it.py'], lines)
E AssertionError: 3 != 4
@yury-primer you mention this might be related to #663. Are you on Windows?
Never mind, I can see that the problem happens on Mac also.
Yeah, I forgot to mention this is on Mac OS. Thanks for looking into it!
I've written an issue on gevent to get some help: https://github.com/gevent/gevent/issues/1471
Hi,
I ran into this issue and did some debugging. I came across the bug when using sqlalchemy asyncio, that uses greenlet under the hood (see #1216 ).
I created a test that tested this logic:
def calculate() -> int:
def coroutine() -> int:
print("COVERAGE TEST Inside coroutine")
return 5
print("COVERAGE TEST cov - Spawning greenlet")
greenlet = gevent.spawn(coroutine)
print("COVERAGE TEST cov - Spawning join")
gevent.joinall([greenlet])
print("COVERAGE TEST nocov - Spawning joined")
result = greenlet.get() # this line runs, but is not covered
print(f"COVERAGE TEST nocov - Returning result {result}")
return result
As noted above, the code after `gevent.joinall([greenlet]) does not get any coverage.
I ran the coverage with the trace and logging in the CTracer. I noted that the covered lines have depth 0, but the lines that flag as no-coverage have depth 1. See the attached file for the complete dump.
621d6af0 trace: f:61ff2050 LINE @ projects/covertest/covertest/logic.py 11
621d6af0: 0 11 projects/covertest/covertest/logic.py line
COVERAGE TEST cov - Spawning join
621d6af0 trace: f:61ff2050 LINE @ projects/covertest/covertest/logic.py 12
621d6af0: 0 12 projects/covertest/covertest/logic.py line
Depth = 0, is present in the coverage report.
COVERAGE TEST nocov - Spawning joined
621d6af0 trace: f:61ff2050 LINE @ projects/covertest/covertest/logic.py 14
621d6af0: 1 14 projects/covertest/covertest/logic.py line
621d6af0 trace: f:61ff2050 LINE @ projects/covertest/covertest/logic.py 15
621d6af0: 1 15 projects/covertest/covertest/logic.py line
Depth = 1 which should be 0 I guess. Not present in the coverage report. I think that the gevent.joinall runs the rest of the code as a greenlet or something. I dont know. I pushed and popped the CALL / RET mentally and also came up with depth 1, so there is not much coverage can do I think. Perhaps it should record even if depth = 1?
Alright, I figured it out. Adding the greenlet to the concurrency options works! That was almost to easy.
.coveragerc:
[run]
concurrency=greenlet