Repository: cython/cython

Issue:
Cython.Coverage produces error when using gevent
Hi,

I build a simple pyx with ``gevent`` module (https://github.com/wjsi/test_cython_cov/blob/master/test_cov.pyx) and try to use Cython.Coverage to get code coverage, it produces an error when ``coverage report`` is executed:

```
$ coverage run test_file.py
$ coverage report
Plugin 'Cython.Coverage.Plugin' did not provide a file reporter for '/Users/wenjun.swj/miniconda3/lib/python3.7/site-packages/gevent/_hub_local.py'.
```

When gevent is removed, for instance, replaced with ``time`` module, no errors raised and the report is shown normally.

I uploaded all test files in https://github.com/wjsi/test_cython_cov. I'm running on MacOS 10.14 with Anaconda Python 3.7.1, cython==0.29.2 and coverage==4.5.2.


Discussion:

Resolving patch:
diff --git a/Cython/Coverage.py b/Cython/Coverage.py
--- a/Cython/Coverage.py
+++ b/Cython/Coverage.py
@@ -74,7 +74,7 @@ def file_tracer(self, filename):
         if c_file is None:
             c_file, py_file = self._find_source_files(filename)
             if not c_file:
-                return None
+                return None  # unknown file
 
             # parse all source file paths and lines from C file
             # to learn about all relevant source files right away (pyx/pxi/pxd)
@@ -82,7 +82,9 @@ def file_tracer(self, filename):
             #        is not from the main .pyx file but a file with a different
             #        name than the .c file (which prevents us from finding the
             #        .c file)
-            self._parse_lines(c_file, filename)
+            _, code = self._parse_lines(c_file, filename)
+            if code is None:
+                return None  # no source found
 
         if self._file_path_map is None:
             self._file_path_map = {}


Test patch:
diff --git a/tests/run/coverage_installed_pkg.srctree b/tests/run/coverage_installed_pkg.srctree
new file mode 100644
--- /dev/null
+++ b/tests/run/coverage_installed_pkg.srctree
@@ -0,0 +1,71 @@
+# mode: run
+# tag: coverage,trace
+
+"""
+PYTHON setup.py build_ext -i
+PYTHON -c "import shutil; shutil.move('ext_src/ext_pkg', 'ext_pkg')"
+PYTHON -m coverage run coverage_test.py
+PYTHON -m coverage report
+"""
+
+######## setup.py ########
+from distutils.core import setup, Extension
+from Cython.Build import cythonize
+
+setup(ext_modules = cythonize([
+    'pkg/*.pyx',
+]))
+
+setup(
+    name='ext_pkg',
+    package_dir={'': 'ext_src'},
+    ext_modules = cythonize([
+        Extension('ext_pkg._mul', ['ext_src/ext_pkg/mul.py'])
+    ]),
+)
+
+
+######## .coveragerc ########
+[run]
+plugins = Cython.Coverage
+
+
+######## pkg/__init__.py ########
+from .test_ext_import import test_add
+
+
+######## pkg/test_ext_import.pyx ########
+# cython: linetrace=True
+# distutils: define_macros=CYTHON_TRACE=1
+
+import ext_pkg
+
+
+cpdef test_add(int a, int b):
+    return a + ext_pkg.test_mul(b, 2)
+
+
+######## ext_src/ext_pkg/__init__.py ########
+from .mul import test_mul
+
+
+######## ext_src/ext_pkg/mul.py ########
+from __future__ import absolute_import
+
+
+def test_mul(a, b):
+     return a * b
+
+
+try:
+    from ._mul import *
+except ImportError:
+    pass
+
+
+######## coverage_test.py ########
+
+from pkg import test_add
+
+
+assert 5 == test_add(1, 2)