Skip to content
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

Covered lines marked as missing after gevent.joinall. #841

Open
yury-primer opened this issue Aug 21, 2019 · 7 comments
Open

Covered lines marked as missing after gevent.joinall. #841

yury-primer opened this issue Aug 21, 2019 · 7 comments
Labels
bug Something isn't working concurrency exotic Unusual execution environment

Comments

@yury-primer
Copy link

Describe the bug
Lines after a call to gevent.joinall() are reported as not covered even though they get run.

To Reproduce

  1. What version of Python are you running?
    3.6.5

  2. What versions of what packages do you have installed? The output of pip freeze is very helpful.

coverage==4.5.4
gevent==1.4.0
greenlet==0.4.15
  1. 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()
  1. What commands did you run?
    coverage run --include=test_foo.py --concurrency=gevent -m test_foo
    The above command should fail. Change the 4 to a 5 to 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.

@yury-primer yury-primer added the bug Something isn't working label Aug 21, 2019
@yury-primer
Copy link
Author

yury-primer commented Sep 3, 2019

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

@nedbat
Copy link
Owner

nedbat commented Oct 23, 2019

@yury-primer you mention this might be related to #663. Are you on Windows?

@nedbat
Copy link
Owner

nedbat commented Oct 23, 2019

Never mind, I can see that the problem happens on Mac also.

nedbat added a commit that referenced this issue Oct 23, 2019
@yury-primer
Copy link
Author

Yeah, I forgot to mention this is on Mac OS. Thanks for looking into it!

@nedbat
Copy link
Owner

nedbat commented Oct 23, 2019

I've written an issue on gevent to get some help: gevent/gevent#1471

@nedbat nedbat added exotic Unusual execution environment and removed bug Something isn't working labels Jan 15, 2020
@nedbat nedbat added concurrency bug Something isn't working labels Nov 21, 2021
@klaasjanelzinga
Copy link

klaasjanelzinga commented Jun 29, 2022

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?

trace.txt

@klaasjanelzinga
Copy link

klaasjanelzinga commented Jun 29, 2022

Alright, I figured it out. Adding the greenlet to the concurrency options works! That was almost to easy.

.coveragerc:

[run]
concurrency=greenlet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working concurrency exotic Unusual execution environment
Projects
None yet
Development

No branches or pull requests

3 participants