Conversation
Greptile SummaryAdds a new
Confidence Score: 4/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Run pytest with JUnit XML output] --> B[Parse JUnit XML for test cases]
B --> C[For each test case]
C --> D[Convert classname to file path]
D --> E[AST parse source file & find function lines]
E --> F[git blame on function line range]
F --> G[Attribute duration proportionally by author line count]
G --> C
C --> H[Sort authors by time-per-line]
H --> I[Print ranked leaderboard table]
Last reviewed commit: 4aa2910 |
| for node in ast.walk(tree): | ||
| if class_name and isinstance(node, ast.ClassDef) and node.name == class_name: | ||
| for item in node.body: | ||
| if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| if item.name == func_name: | ||
| return item.lineno, item.end_lineno | ||
| elif not class_name and isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): | ||
| if node.name == func_name: | ||
| return node.lineno, node.end_lineno |
There was a problem hiding this comment.
ast.walk may match class methods for module-level tests
When class_name is None (i.e. the test is a module-level function), ast.walk(tree) traverses every node in the AST including methods inside classes. If a class method happens to share the same name as a module-level test function, this will return whichever one ast.walk yields first — which has no guaranteed order. This could attribute the wrong line range to a test.
Consider restricting the no-class branch to only search tree.body (top-level statements) instead of walking the full tree:
| for node in ast.walk(tree): | |
| if class_name and isinstance(node, ast.ClassDef) and node.name == class_name: | |
| for item in node.body: | |
| if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): | |
| if item.name == func_name: | |
| return item.lineno, item.end_lineno | |
| elif not class_name and isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): | |
| if node.name == func_name: | |
| return node.lineno, node.end_lineno | |
| def find_function_lines(tree, class_name, func_name): | |
| """Return (start_line, end_line) of a test function in an AST.""" | |
| for node in ast.walk(tree): | |
| if class_name and isinstance(node, ast.ClassDef) and node.name == class_name: | |
| for item in node.body: | |
| if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)): | |
| if item.name == func_name: | |
| return item.lineno, item.end_lineno | |
| if not class_name: | |
| for node in tree.body: | |
| if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): | |
| if node.name == func_name: | |
| return node.lineno, node.end_lineno | |
| return None, None |
leshy
left a comment
There was a problem hiding this comment.
approved without a review this is a fun helper
Problem
Closes DIM-XXX
Solution
Breaking Changes
How to Test
Contributor License Agreement