summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Behnel <stefan_ml@behnel.de>2019-03-02 10:05:55 +0100
committerStefan Behnel <stefan_ml@behnel.de>2019-03-02 10:05:55 +0100
commitd6341966d991e9077446258e4c727341098ed84a (patch)
tree9e18a197b5ebef76fe5126f64417683684a3c6fe
parent869044783972599839e4915db742687609587dbb (diff)
downloadcython-d6341966d991e9077446258e4c727341098ed84a.tar.gz
Refactor parsing function in code coverage plugin to split the actual parsing code from the lookup code.
-rw-r--r--Cython/Coverage.py105
1 files changed, 56 insertions, 49 deletions
diff --git a/Cython/Coverage.py b/Cython/Coverage.py
index 43b1bc9c5..87266461d 100644
--- a/Cython/Coverage.py
+++ b/Cython/Coverage.py
@@ -82,7 +82,7 @@ class Plugin(CoveragePlugin):
# 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)
- _, code = self._parse_lines(c_file, filename)
+ _, code = self._read_source_lines(c_file, filename)
if code is None:
return None # no source found
@@ -104,7 +104,7 @@ class Plugin(CoveragePlugin):
c_file, _ = self._find_source_files(filename)
if not c_file:
return None # unknown file
- rel_file_path, code = self._parse_lines(c_file, filename)
+ rel_file_path, code = self._read_source_lines(c_file, filename)
if code is None:
return None # no source found
return CythonModuleReporter(c_file, filename, rel_file_path, code)
@@ -170,14 +170,14 @@ class Plugin(CoveragePlugin):
for filename in os.listdir(dir_path):
ext = splitext(filename)[1].lower()
if ext in C_FILE_EXTENSIONS:
- self._parse_lines(os.path.join(dir_path, filename), source_file)
+ self._read_source_lines(os.path.join(dir_path, filename), source_file)
if source_file in self._c_files_map:
return
# not found? then try one package up
if is_package_dir(dir_path):
self._find_c_source_files(os.path.dirname(dir_path), source_file)
- def _parse_lines(self, c_file, sourcefile):
+ def _read_source_lines(self, c_file, sourcefile):
"""
Parse a Cython generated C/C++ source file and find the executable lines.
Each executable line starts with a comment header that states source file
@@ -188,51 +188,7 @@ class Plugin(CoveragePlugin):
if c_file in self._parsed_c_files:
code_lines = self._parsed_c_files[c_file]
else:
- match_source_path_line = re.compile(r' */[*] +"(.*)":([0-9]+)$').match
- match_current_code_line = re.compile(r' *[*] (.*) # <<<<<<+$').match
- match_comment_end = re.compile(r' *[*]/$').match
- match_trace_line = re.compile(r' *__Pyx_TraceLine\(([0-9]+),').match
- not_executable = re.compile(
- r'\s*c(?:type)?def\s+'
- r'(?:(?:public|external)\s+)?'
- r'(?:struct|union|enum|class)'
- r'(\s+[^:]+|)\s*:'
- ).match
-
- code_lines = defaultdict(dict)
- executable_lines = defaultdict(set)
- current_filename = None
- with open(c_file) as lines:
- lines = iter(lines)
- for line in lines:
- match = match_source_path_line(line)
- if not match:
- if '__Pyx_TraceLine(' in line and current_filename is not None:
- trace_line = match_trace_line(line)
- if trace_line:
- executable_lines[current_filename].add(int(trace_line.group(1)))
- continue
- filename, lineno = match.groups()
- current_filename = filename
- lineno = int(lineno)
- for comment_line in lines:
- match = match_current_code_line(comment_line)
- if match:
- code_line = match.group(1).rstrip()
- if not_executable(code_line):
- break
- code_lines[filename][lineno] = code_line
- break
- elif match_comment_end(comment_line):
- # unexpected comment format - false positive?
- break
-
- # Remove lines that generated code but are not traceable.
- for filename, lines in code_lines.items():
- dead_lines = set(lines).difference(executable_lines.get(filename, ()))
- for lineno in dead_lines:
- del lines[lineno]
-
+ code_lines = self._parse_cfile_lines(c_file)
self._parsed_c_files[c_file] = code_lines
if self._c_files_map is None:
@@ -246,6 +202,57 @@ class Plugin(CoveragePlugin):
return (None,) * 2 # e.g. shared library file
return self._c_files_map[sourcefile][1:]
+ def _parse_cfile_lines(self, c_file):
+ """
+ Parse a C file and extract all source file lines that generated executable code.
+ """
+ match_source_path_line = re.compile(r' */[*] +"(.*)":([0-9]+)$').match
+ match_current_code_line = re.compile(r' *[*] (.*) # <<<<<<+$').match
+ match_comment_end = re.compile(r' *[*]/$').match
+ match_trace_line = re.compile(r' *__Pyx_TraceLine\(([0-9]+),').match
+ not_executable = re.compile(
+ r'\s*c(?:type)?def\s+'
+ r'(?:(?:public|external)\s+)?'
+ r'(?:struct|union|enum|class)'
+ r'(\s+[^:]+|)\s*:'
+ ).match
+
+ code_lines = defaultdict(dict)
+ executable_lines = defaultdict(set)
+ current_filename = None
+
+ with open(c_file) as lines:
+ lines = iter(lines)
+ for line in lines:
+ match = match_source_path_line(line)
+ if not match:
+ if '__Pyx_TraceLine(' in line and current_filename is not None:
+ trace_line = match_trace_line(line)
+ if trace_line:
+ executable_lines[current_filename].add(int(trace_line.group(1)))
+ continue
+ filename, lineno = match.groups()
+ current_filename = filename
+ lineno = int(lineno)
+ for comment_line in lines:
+ match = match_current_code_line(comment_line)
+ if match:
+ code_line = match.group(1).rstrip()
+ if not_executable(code_line):
+ break
+ code_lines[filename][lineno] = code_line
+ break
+ elif match_comment_end(comment_line):
+ # unexpected comment format - false positive?
+ break
+
+ # Remove lines that generated code but are not traceable.
+ for filename, lines in code_lines.items():
+ dead_lines = set(lines).difference(executable_lines.get(filename, ()))
+ for lineno in dead_lines:
+ del lines[lineno]
+ return code_lines
+
class CythonModuleTracer(FileTracer):
"""