1313
1414import json
1515import logging
16- import os
1716import re
1817from pathlib import Path
19- from typing import TYPE_CHECKING
18+ from typing import TYPE_CHECKING , Any
2019
2120if TYPE_CHECKING :
2221 from tree_sitter import Node
@@ -73,11 +72,7 @@ def __init__(self, output_file: Path, warmup_iterations: int = DEFAULT_WARMUP_IT
7372 # === Agent-based profiling (bytecode instrumentation) ===
7473
7574 def generate_agent_config (
76- self ,
77- source : str ,
78- file_path : Path ,
79- functions : list [FunctionInfo ],
80- config_output_path : Path ,
75+ self , source : str , file_path : Path , functions : list [FunctionInfo ], config_output_path : Path
8176 ) -> Path :
8277 """Generate config JSON for the profiler agent.
8378
@@ -104,7 +99,12 @@ class name, then writes a config JSON that the agent uses to know which
10499 for line_num in range (func .starting_line , func .ending_line + 1 ):
105100 if 1 <= line_num <= len (lines ):
106101 content = lines [line_num - 1 ].strip ()
107- if content and not content .startswith ("//" ) and not content .startswith ("/*" ) and not content .startswith ("*" ):
102+ if (
103+ content
104+ and not content .startswith ("//" )
105+ and not content .startswith ("/*" )
106+ and not content .startswith ("*" )
107+ ):
108108 key = f"{ file_path .as_posix ()} :{ line_num } "
109109 line_contents [key ] = content
110110
@@ -120,12 +120,7 @@ class name, then writes a config JSON that the agent uses to know which
120120 config = {
121121 "outputFile" : str (self .output_file ),
122122 "warmupIterations" : self .warmup_iterations ,
123- "targets" : [
124- {
125- "className" : class_name ,
126- "methods" : method_targets ,
127- }
128- ],
123+ "targets" : [{"className" : class_name , "methods" : method_targets }],
129124 "lineContents" : line_contents ,
130125 }
131126
@@ -143,7 +138,9 @@ def build_javaagent_arg(self, config_path: Path) -> str:
143138
144139 # === Source-level instrumentation ===
145140
146- def instrument_source (self , source : str , file_path : Path , functions : list [FunctionInfo ], analyzer = None ) -> str :
141+ def instrument_source (
142+ self , source : str , file_path : Path , functions : list [FunctionInfo ], analyzer : Any = None
143+ ) -> str :
147144 """Instrument Java source code with line profiling.
148145
149146 Injects a profiler class and per-line hit() calls directly into the source.
@@ -159,7 +156,7 @@ def instrument_source(self, source: str, file_path: Path, functions: list[Functi
159156
160157 """
161158 # Initialize line contents map
162- self .line_contents = {}
159+ self .line_contents : dict [ str , str ] = {}
163160
164161 lines = source .splitlines (keepends = True )
165162
@@ -327,7 +324,7 @@ class {self.profiler_class} {{
327324}}
328325"""
329326
330- def instrument_function (self , func : FunctionInfo , lines : list [str ], file_path : Path , analyzer ) -> list [str ]:
327+ def instrument_function (self , func : FunctionInfo , lines : list [str ], file_path : Path , analyzer : Any ) -> list [str ]:
331328 """Instrument a single function with line profiling.
332329
333330 Args:
@@ -446,7 +443,7 @@ def find_executable_lines(self, node: Node) -> set[int]:
446443 # === Result parsing (shared by both approaches) ===
447444
448445 @staticmethod
449- def parse_results (profile_file : Path ) -> dict :
446+ def parse_results (profile_file : Path ) -> dict [ str , Any ] :
450447 """Parse line profiling results from the agent's JSON output.
451448
452449 Returns the same format as parse_line_profile_test_output.parse_line_profile_results()
@@ -520,9 +517,9 @@ def parse_results(profile_file: Path) -> dict:
520517 for fp , line_stats in lines_by_file .items ():
521518 sorted_stats = sorted (line_stats , key = lambda t : t [0 ])
522519 if sorted_stats :
523- grouped_timings [(fp , sorted_stats [0 ][0 ], os . path . basename (fp ))] = sorted_stats
520+ grouped_timings [(fp , sorted_stats [0 ][0 ], Path (fp ). name )] = sorted_stats
524521
525- result : dict = {"timings" : grouped_timings , "unit" : 1e-9 , "line_contents" : line_contents }
522+ result : dict [ str , Any ] = {"timings" : grouped_timings , "unit" : 1e-9 , "line_contents" : line_contents }
526523 result ["str_out" ] = format_line_profile_results (result , line_contents )
527524 return result
528525
@@ -531,15 +528,14 @@ def parse_results(profile_file: Path) -> dict:
531528 return {"timings" : {}, "unit" : 1e-9 , "str_out" : "" }
532529
533530
534- def load_method_ranges (
535- profile_file : Path ,
536- ) -> tuple [list [tuple [str , str , int , int ]], dict [str , str ]]:
531+ def load_method_ranges (profile_file : Path ) -> tuple [list [tuple [str , str , int , int ]], dict [str , str ]]:
537532 """Load method ranges and line contents from the agent config file.
538533
539534 Returns:
540535 (method_ranges, config_line_contents) where method_ranges is a list of
541536 (source_file, method_name, start_line, end_line) and config_line_contents
542537 is the lineContents dict from the config (key: "file:line", value: source text).
538+
543539 """
544540 config_path = profile_file .with_suffix (".config.json" )
545541 if not config_path .exists ():
@@ -549,12 +545,7 @@ def load_method_ranges(
549545 ranges = []
550546 for target in config .get ("targets" , []):
551547 for method in target .get ("methods" , []):
552- ranges .append ((
553- method .get ("sourceFile" , "" ),
554- method ["name" ],
555- method ["startLine" ],
556- method ["endLine" ],
557- ))
548+ ranges .append ((method .get ("sourceFile" , "" ), method ["name" ], method ["startLine" ], method ["endLine" ]))
558549 return ranges , config .get ("lineContents" , {})
559550 except Exception :
560551 return [], {}
@@ -571,7 +562,7 @@ def find_method_for_line(
571562 for source_file , method_name , start_line , end_line in method_ranges :
572563 if file_path == source_file and start_line <= line_num <= end_line :
573564 return method_name , start_line
574- return os . path . basename (file_path ), line_num
565+ return Path (file_path ). name , line_num
575566
576567
577568def find_agent_jar () -> Path | None :
@@ -580,16 +571,7 @@ def find_agent_jar() -> Path | None:
580571 Checks local Maven repo, package resources, and development build directory.
581572 """
582573 # Check local Maven repository first (fastest)
583- m2_jar = (
584- Path .home ()
585- / ".m2"
586- / "repository"
587- / "com"
588- / "codeflash"
589- / "codeflash-runtime"
590- / "1.0.0"
591- / AGENT_JAR_NAME
592- )
574+ m2_jar = Path .home () / ".m2" / "repository" / "com" / "codeflash" / "codeflash-runtime" / "1.0.0" / AGENT_JAR_NAME
593575 if m2_jar .exists ():
594576 return m2_jar
595577
@@ -599,9 +581,7 @@ def find_agent_jar() -> Path | None:
599581 return resources_jar
600582
601583 # Check development build directory
602- dev_jar = (
603- Path (__file__ ).parent .parent .parent .parent / "codeflash-java-runtime" / "target" / AGENT_JAR_NAME
604- )
584+ dev_jar = Path (__file__ ).parent .parent .parent .parent / "codeflash-java-runtime" / "target" / AGENT_JAR_NAME
605585 if dev_jar .exists ():
606586 return dev_jar
607587
@@ -624,7 +604,7 @@ def resolve_internal_class_name(file_path: Path, source: str) -> str:
624604
625605
626606def format_line_profile_results (
627- results : dict , line_contents : dict [tuple [str , int ], str ] | None = None
607+ results : dict [ str , Any ] , line_contents : dict [tuple [str , int ], str ] | None = None
628608) -> str :
629609 """Format line profiling results using the same tabulate pipe format as Python.
630610
0 commit comments