Coverage >= 5 cannot handle Cython `public` or `readonly` members in `.pxd` file
Describe the bug
After upgrading to coverage >= 5,
coverage run outputs this error message for a Cython pxd file with certain conditions I describe below:
Can't add file tracer data for unmeasured file '/home/kai/tst/cy/kai.pxd'
Then coverage report fails with this error:
Couldn't parse '/home/kai/tst/cy/kai.pyx' as Python source: 'invalid syntax' at line 1
To Reproduce
- What version of coverage.py are you using? The output of
coverage debug sysis helpful.
-- sys -------------------------------------------------------
version: 5.1
coverage: /home/kai/tst/lib64/python3.7/site-packages/coverage/__init__.py
tracer: -none-
CTracer: available
plugins.file_tracers: Cython.Coverage.Plugin
plugins.configurers: -none-
plugins.context_switchers: -none-
configs_attempted: .coveragerc
setup.cfg
configs_read: /home/kai/tst/setup.cfg
config_file: /home/kai/tst/setup.cfg
config_contents: '[coverage:run]\n# http://blog.behnel.de/posts/coverage-analysis-for-cython-modules.html\nplugins = Cython.Coverage\n'
data_file: -none-
python: 3.7.6 (default, Feb 26 2020, 20:54:15) [GCC 7.3.1 20180712 (Red Hat 7.3.1-6)]
platform: Linux-4.19.76-linuxkit-x86_64-with-glibc2.2.5
implementation: CPython
executable: /home/kai/tst/bin/python3
def_encoding: utf-8
fs_encoding: utf-8
pid: 9474
cwd: /home/kai/tst
path: /home/kai/tst/bin
/usr/lib64/python37.zip
/usr/lib64/python3.7
/usr/lib64/python3.7/lib-dynload
/home/kai/tst/lib64/python3.7/site-packages
/home/kai/tst/lib/python3.7/site-packages
environment: -none-
command_line: /home/kai/tst/bin/coverage debug sys
sqlite3_version: 2.6.0
sqlite3_sqlite_version: 3.7.17
sqlite3_temp_store: 0
sqlite3_compile_options: DISABLE_DIRSYNC
ENABLE_COLUMN_METADATA
ENABLE_FTS3
ENABLE_RTREE
ENABLE_UNLOCK_NOTIFY
SECURE_DELETE
TEMP_STORE=1
THREADSAFE=1
- What versions of what packages do you have installed? The output of
pip freezeis helpful.
coverage==5.1
Cython==0.29.16
Tested with coverage 4.5.4 (OK), 5.0.4 (fail), 5.1 (fail)
- What code are you running? Give us a specific commit of a specific repo that we can check out.
cy/__init__.pxd(empty file)
cy/kai.pxd
cdef class Kai:
cdef readonly bint x
cpdef bint get_x(self)
cy/kai.pyx
cdef class Kai:
def __init__(self):
self.x = False
cpdef bint get_x(self):
return self.x
cy/setup.py
from Cython.Build import cythonize
from distutils.core import setup
from distutils.extension import Extension
define_macros = [('CYTHON_TRACE', '1')]
compiler_directives = {'always_allow_keywords': True, 'profile': True, 'linetrace': True}
extensions = [
Extension('cy.kai', ['cy/kai.pyx'], define_macros=define_macros),
]
setup(ext_modules=cythonize(extensions, nthreads=4, annotate=True, language_level="3", compiler_directives=compiler_directives))
runner.py
from cy import kai
k = kai.Kai()
print(k.get_x())
print(k.x)
- What commands did you run?
$ python3 cy/setup.py build_ext --inplace
$ coverage run runner.py
$ coverage report
Expected behavior Coverage statistics for pyx and pxd files
Additional context The problem manifests itself only when
- coverage >= 5
publicorreadonlymembers are in.pxdfile, separate from.pyxfile, and- runner.py actually accesses the members directly
My understanding is readonly and public modifiers make Cython define wrapper functions for them transparently. coverage >= 5 seem to play badly with them.
Thanks, I see the error from "coverage report", but I don't see the message you show for "coverage run". Do you have any other context I need?
I got it from the "debug sys" output you showed: you have a .coveragerc file with this:
[coverage:run]
# http://blog.behnel.de/posts/coverage-analysis-for-cython-modules.html
plugins = Cython.Coverage
Now I see the same results you do.
xref cython/cython#3515 tentative workaround is to add a dummy inline function in the affected .pxd file
The best workaround I could find for this was to edit the .coveragerc file adding the relative path of all Cython .pxd files in the list of files to be ignored by coverage. More details on https://github.com/saullocastro/composites:
[run]
plugins = Cython.Coverage
source = composites
omit =
composites/core.pxd
I was able to bisect the commit of the issue: https://github.com/nedbat/coveragepy/commit/106828c2cc8bbce1e5fb31c6a89ea3ac025225c1#. The root cause is following: reproducer described in this issue is yielding following data in parameter d:
https://github.com/nedbat/coveragepy/blob/5ec587caf0bd670a0af34ad5b766119689b91b56/coverage/collector.py#L417
{
'/Users/matus/dev/cython/test/coverage/runner.py': {1: None, 3: None, 4: None, 5: None},
'/Users/matus/dev/cython/test/coverage/cy/kai.pyx': {5: None, 1: None, 16: None, 11: None, 3: None, 6: None},
'/Users/matus/dev/cython/test/coverage/cy/kai.pxd': {}
}
It can be seen that kai.pxd file is mapped to empty dictionary. This is obviously filtered in following line: https://github.com/nedbat/coveragepy/blob/5ec587caf0bd670a0af34ad5b766119689b91b56/coverage/collector.py#L434
This is causing that kai.pxd is not inserted in database here:
https://github.com/nedbat/coveragepy/blob/5ec587caf0bd670a0af34ad5b766119689b91b56/coverage/sqldata.py#L383-L394
When in the later stage the data needs to be fetched from database, the final error is raised due missing record in DB:
https://github.com/nedbat/coveragepy/blob/5ec587caf0bd670a0af34ad5b766119689b91b56/coverage/sqldata.py#L549-L553
As a proof of concept I have created a PR which seems to fix the issue. I am not familiar with coverage codebase so I cannot tell if this is proper fix or the issue needs to be fixed somewhere else.
This is now released as part of coverage 6.4.3.