diff --git a/CHANGELOG.md b/CHANGELOG.md index d5fe327..905747c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.0.3] - Unreleased + +### Changed +- #52: Method mapping code now doesn't use AST's endline_no property to support older python versions +- #53: Ignore traced commands from code without a class name + ## [4.0.2] - 2024-08-16 ### Fixed diff --git a/cls/TestCoverage/Utils.cls b/cls/TestCoverage/Utils.cls index ee5256b..9780288 100644 --- a/cls/TestCoverage/Utils.cls +++ b/cls/TestCoverage/Utils.cls @@ -451,9 +451,8 @@ ClassMethod GetPythonMethodMapping(pDocumentText) [ Language = python ] import iris import ast source_lines = iris.cls('%SYS.Python').ToList(pDocumentText) - source_lines = [line + "\n" for line in source_lines] # contains a list of each line of the source code - source = ''.join(source_lines) + source = '\n'.join(source_lines) tree = ast.parse(source) line_function_map = [None] * (len(source_lines)+2) method_map = {} # dictionary from the method name to its start and ending line number @@ -473,22 +472,21 @@ ClassMethod GetPythonMethodMapping(pDocumentText) [ Language = python ] def visit_FunctionDef(self, node): if self.outermost_function is None: self.outermost_function = node.name - method_map[node.name] = (node.lineno-1, node.end_lineno-1) + start_line = node.lineno + end_line = self.get_end_line(node) + method_map[node.name] = (start_line, end_line) self.current_function = node.name - for lineno in range(node.lineno, node.end_lineno + 1): + for lineno in range(node.lineno, self.get_end_line(node) + 1): line_function_map[lineno-1] = self.outermost_function self.generic_visit(node) self.current_function = None if self.outermost_function == node.name: self.outermost_function = None - - # preprocessing the ending line number for each function - tree_with_line_numbers = ast.increment_lineno(tree, n=1) - for node in ast.walk(tree_with_line_numbers): - if isinstance(node, ast.FunctionDef): - node.end_lineno = node.body[-1].end_lineno + @staticmethod + def get_end_line(node): + return max(child.lineno for child in ast.walk(node) if hasattr(child, 'lineno')) FunctionMapper().visit(tree) return (line_function_map, method_map) diff --git a/cls/TestCoverage/Utils/LineByLineMonitor.cls b/cls/TestCoverage/Utils/LineByLineMonitor.cls index 1e335d5..5b5b69f 100644 --- a/cls/TestCoverage/Utils/LineByLineMonitor.cls +++ b/cls/TestCoverage/Utils/LineByLineMonitor.cls @@ -76,10 +76,10 @@ ClassMethod PyStartWithScope(pCoverageClasses As %List) [ Language = python ] # extracts frame code code = frame.f_code # extracts calling function name and the class that the function is in - class_name = frame.f_globals['__name__'] + class_name = frame.f_globals.get('__name__', None) # Use get to avoid KeyError # extracts the line number line_no = frame.f_lineno - if class_name in tCoverageClasses and line_no > 1: # if this is in a covered class + if class_name and class_name in tCoverageClasses and line_no > 1: # if this is in a covered class tGlob = iris.gref('^IRIS.Temp.TestCoveragePY') # python doesn't have macros -- this is $$$PyMonitorResults # $$$PyMonitorResults(classname, linenumber) = the number of times that linenumber in that class was covered diff --git a/module.xml b/module.xml index 3b9f9ec..4cf105b 100644 --- a/module.xml +++ b/module.xml @@ -2,7 +2,7 @@ TestCoverage - 4.0.2 + 4.0.3 Run your typical ObjectScript %UnitTest tests and see which lines of your code are executed. Includes Cobertura-style reporting for use in continuous integration tools. module