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

covimerage.CoveragePlugin not supported with PyTracer after upgrade to ubuntu 24.04 #104

Open
bstaletic opened this issue Aug 31, 2024 · 2 comments

Comments

@bstaletic
Copy link

YouCompleteMe has been using covimerage for years.
I'm trying to upgrade our CI to ubuntu 24.04 and am getting this error when running coverage:

coverage.py warning: Plugin file tracers (covimerage.CoveragePlugin) aren't supported with PyTracer

We have this in our test_requirements.txt:

coverage              <5.0
click                 <8.0.0
covimerage            >= 0.2.0

The click and coverage constraints were added to get covimerage working properly.
YCM pull request can be found here:
ycm-core/YouCompleteMe#4260

Comparing the CI logs for master and for my pull request, there is a difference in what pip spits out when installing coverage.

Master:

Building wheels for collected packages: coverage
  Building wheel for coverage (setup.py): started
  Building wheel for coverage (setup.py): finished with status 'done'
  Created wheel for coverage: filename=coverage-4.5.4-cp310-cp310-linux_x86_64.whl size=206478 sha256=7a52431a3d484347b24316972c7e5dc2835f8077195383b8d2e58415e1532c95
  Stored in directory: /root/.cache/pip/wheels/24/3b/59/8b5b481efbac60694af4d7d281e8b27461384fcbd51ae53c8e
Successfully built coverage

Pull request

Building wheels for collected packages: coverage
  Building wheel for coverage (setup.py): started
  Building wheel for coverage (setup.py): finished with status 'done'
  Created wheel for coverage: filename=coverage-4.5.4-py3-none-any.whl size=170500 sha256=cfc74044c055dc6b6888ccaf26f72c8f1f852202ca8ae29a64adb504a28972c0
  Stored in directory: /root/.cache/pip/wheels/4e/09/63/e527151646074292e08bbcf04b648bd99319503c98f64eee20
Successfully built coverage
@bstaletic
Copy link
Author

The -none-any variant of the wheel gets selected because there is no -cp312 variant, because coveragepy 4.5.4 is old.
The -none-any variant of the coveragepy wheel contains no coverage.tracer module.
That means no coverage.tracer.CTracer.
That means no plugins.

Essentially, this comes down to needing covimerage to support newer coveragepy.

@bstaletic
Copy link
Author

I got it working. Here's the diff

diff --git a/covimerage/__init__.py b/covimerage/__init__.py
index d0ee675..1e8b0c7 100755
--- a/covimerage/__init__.py
+++ b/covimerage/__init__.py
@@ -195,22 +195,8 @@ class MergedProfiles(object):
         if not line_counts:
             logger.warning('Not writing coverage file: no data to report!')
             return False
-
-        if isinstance(data_file, string_types):
-            logger.info('Writing coverage file %s.', data_file)
-            try:
-                write_file = cov_data.write_file
-            except AttributeError:
-                # coveragepy 5
-                write_file = cov_data._write_file
-            write_file(data_file)
-        else:
-            try:
-                filename = data_file.name
-            except AttributeError:
-                filename = str(data_file)
-            logger.info('Writing coverage file %s.', filename)
-            cov_data.write_fileobj(data_file)
+        
+        cov_data.write()
         return True
 
 
@@ -331,7 +317,7 @@ class Profile(object):
             # are joined, while script lines might be spread
             # across several lines (prefixed with \).
             script_source = s_line.line
-            if script_source != f_line.line:
+            if script_source != f_line.line.lstrip():
                 while True:
                     peek = script.lines[script_lnum + f_lnum + 1]
                     m = RE_CONTINUING_LINE.match(peek.line)
@@ -339,7 +325,7 @@ class Profile(object):
                         script_source += peek.line[m.end():]
                         script_lnum += 1
                         continue
-                    if script_source == f_line.line:
+                    if script_source == f_line.line.lstrip():
                         break
                     return False
         return True
@@ -379,13 +365,12 @@ class Profile(object):
             if in_script or in_function:
                 lnum += 1
                 try:
-                    count, total_time, self_time = parse_count_and_times(line)
+                    count, total_time, self_time, source_line = parse_count_and_times(line)
                 except Exception as exc:
                     logger.warning(
                         'Could not parse count/times (%s:%d, %r): %r.',
                         self._fstr, plnum, line, exc)
                     continue
-                source_line = line[28:]
 
                 if in_script:
                     if count is None and RE_CONTINUING_LINE.match(source_line):
@@ -447,6 +432,7 @@ class Profile(object):
         while functions:
             prev_count = len(functions)
             for f in functions:
+                #logger.warning('debug - %s - %s - %s', f.name, f.source, f.lines)
                 if self.map_function(f):
                     functions.remove(f)
             new_count = len(functions)
@@ -481,7 +467,7 @@ class Profile(object):
             # are joined, while script lines might be spread
             # across several lines (prefixed with \).
             script_source = s_line.line
-            if script_source != f_line.line:
+            if script_source != f_line.line.lstrip():
                 while True:
                     try:
                         peek = script.lines[script_lnum + f_lnum + 1]
@@ -493,7 +479,7 @@ class Profile(object):
                             script_source += peek.line[m.end():]
                             script_lnum += 1
                             continue
-                    if script_source == f_line.line:
+                    if script_source == f_line.line.lstrip():
                         break
 
                     logger.warning(
@@ -533,25 +519,13 @@ class Profile(object):
 
 
 def parse_count_and_times(line):
-    count = line[0:5]
-    if count == '':
-        return 0, None, None
-    if count == '     ':
-        count = None
-    else:
-        count = int(count)
-    total_time = line[8:16]
-    if total_time == '        ':
-        total_time = None
-    else:
-        total_time = float(total_time)
-    self_time = line[19:27]
-    if self_time == '        ':
-        self_time = None
-    else:
-        self_time = float(self_time)
-
-    return count, total_time, self_time
+    line_regex = re.compile(r'^\s*(\d+)\s+([0-9.]+)\s+([0-9.]+)\s+(.+)$')
+    if match := line_regex.fullmatch( line ):
+        return int( match.group(1) ), float(match.group(2)), float(match.group(3)), match.group(4)
+    line_regex = re.compile(r'^\s*(\d+)\s+([0-9.]+)\s+(.+)$')
+    if match := line_regex.fullmatch( line ):
+        return int( match.group(1) ), float(match.group(2)), None, match.group(3)
+    return None, None, None, line.lstrip()
 
 
 def coverage_init(reg, options):
diff --git a/covimerage/coveragepy.py b/covimerage/coveragepy.py
index a929de4..e64818c 100644
--- a/covimerage/coveragepy.py
+++ b/covimerage/coveragepy.py
@@ -7,16 +7,13 @@ import coverage
 from ._compat import FileNotFoundError
 from .exceptions import CoverageWrapperException
 from .logger import logger
-from .utils import get_fname_and_fobj_and_str, is_executable_line
+from .utils import is_executable_line
 
 RE_EXCLUDED = re.compile(
     r'"\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)')
 
 
-try:
-    from coverage.data import CoverageJsonData as CoveragePyData
-except ImportError:
-    from coverage.data import CoverageData as CoveragePyData
+from coverage.data import CoverageData as CoveragePyData
 
 
 @attr.s(frozen=True)
@@ -33,22 +30,10 @@ class CoverageData(object):
             if self.data_file is not None:
                 raise TypeError('data and data_file are mutually exclusive.')
             return
-        cov_data = CoveragePyData()
+        cov_data = CoveragePyData(self.data_file, suffix = True)
         if self.data_file:
-            fname, fobj, fstr = get_fname_and_fobj_and_str(self.data_file)
             try:
-                if fobj:
-                    try:
-                        read_fileobj = cov_data.read_fileobj
-                    except AttributeError:  # made private in coveragepy 5
-                        read_fileobj = cov_data._read_fileobj
-                    read_fileobj(fobj)
-                else:
-                    try:
-                        read_file = cov_data.read_file
-                    except AttributeError:  # made private in coveragepy 5
-                        read_file = cov_data._read_file
-                    read_file(fname)
+                cov_data.read()
             except coverage.CoverageException as exc:
                 raise CoverageWrapperException(
                     'Coverage could not read data_file: %s' % fstr,
@@ -188,6 +173,16 @@ class FileReporter(coverage.FileReporter):
         return set(lines)
 
 
+class FileTracer(coverage.FileTracer):
+    def __init__(self, filename):
+        self._filename = filename
+    def source_filename(self):
+        return self._filename
+
+
 class CoveragePlugin(coverage.CoveragePlugin):
     def file_reporter(self, filename):
         return FileReporter(filename)
+
+    def file_tracer(self, filename):
+        return FileTracer(filename) if filename.endswith( '.vim' ) else None
diff --git a/setup.py b/setup.py
index 8611061..94b2f2f 100755
--- a/setup.py
+++ b/setup.py
@@ -82,7 +82,7 @@ setup(
     install_requires=[
         'attrs>=16.1.0',
         'click<7.1',
-        'coverage<5.0a6',
+        'coverage',
     ],
     extras_require={
         'testing': DEPS_TESTING,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant