Search code examples
cythoncoverage.py

How to get coverage for lines following call to a Cython module


I have some code which uses a library that happens to use Cython under the hood.

I'm observing that, following any call to a Cython function in that library, no lines in my own code are picked up as having been executed, even though I have tests that are clearly exercising them (I even tested this by putting in a throw to make sure they failed.)

def some_function():
  cython_library.do_something()
  do_something_else()  # Always shows red in coverage

It seems that Cython won't include coverage information unless it's recompiled in a slow coverage mode, but I can't do this as I don't own the library. I tried using the various env vars and plugins suggested by the docs just in case, but to no avail.

Is there a way I can fix this? Unfortunately I can't simply mock out the underlying library completely as my tests would be meaningless if they mocked out the behaviour. I'm willing to consider unpleasant hacks like patching in some logic between my code and the Cython code to force Python to "notice" that it's actually hitting the lines of code, but my naive attempt to do this didn't work as I can't figure out anything I can do that will get Python to notice!

  • The only thing that seems to trigger it to realise it's back in the code is an await, but I can't do that here as these are not coroutines.
  • I tried pushing execution of these functions to a worker thread, but they use thread-locals I can't access.

Solution

  • I dug into this some more, and it turns out to be a bug in the library I'm using that's causing the thread's trace function (from sys) to be replaced erroneously with the trace function that coverage installs in threading. I found this out by comparing the result of sys.gettrace() before and after calling into the library function that was exhibiting the odd tracing behaviour.