Skip to content

Commit 96e231e

Browse files
committed
Merge branch 'fix/java/line-profiler' of github.com:codeflash-ai/codeflash into fix/java/line-profiler
2 parents 9b6d645 + d4653e6 commit 96e231e

4 files changed

Lines changed: 28 additions & 54 deletions

File tree

codeflash/code_utils/tabulate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ def tabulate(
705705
# format rows and columns, convert numeric values to strings
706706
cols = list(izip_longest(*list_of_lists))
707707
numparses = _expand_numparse(disable_numparse, len(cols))
708-
coltypes = [_column_type(col, numparse=np) for col, np in zip(cols, numparses)]
708+
coltypes = [_column_type(col, has_invisible, numparse=np) for col, np in zip(cols, numparses)]
709709
if isinstance(floatfmt, str): # old version
710710
float_formats = len(cols) * [floatfmt] # just duplicate the string to use in each column
711711
else: # if floatfmt is list, tuple etc we have one per column

codeflash/languages/java/line_profiler.py

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313

1414
import json
1515
import logging
16-
import os
1716
import re
1817
from pathlib import Path
19-
from typing import TYPE_CHECKING
18+
from typing import TYPE_CHECKING, Any
2019

2120
if 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

577568
def 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

626606
def 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

codeflash/languages/java/support.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ def _build_runtime_map(self, inv_id_runtimes: dict[InvocationId, list[int]]) ->
273273

274274
def compare_test_results(
275275
self, original_results_path: Path, candidate_results_path: Path, project_root: Path | None = None
276-
) -> tuple[bool, list]:
276+
) -> tuple[bool, list[Any]]:
277277
"""Compare test results between original and candidate code."""
278278
return _compare_test_results(original_results_path, candidate_results_path, project_root=project_root)
279279

@@ -409,10 +409,7 @@ def instrument_source_for_line_profiler(
409409

410410
config_path = line_profiler_output_file.with_suffix(".config.json")
411411
profiler.generate_agent_config(
412-
source=source,
413-
file_path=func_info.file_path,
414-
functions=[func_info],
415-
config_output_path=config_path,
412+
source=source, file_path=func_info.file_path, functions=[func_info], config_output_path=config_path
416413
)
417414

418415
self.line_profiler_agent_arg = profiler.build_javaagent_arg(config_path)
@@ -422,7 +419,7 @@ def instrument_source_for_line_profiler(
422419
logger.exception("Failed to prepare line profiling for %s", func_info.function_name)
423420
return False
424421

425-
def parse_line_profile_results(self, line_profiler_output_file: Path) -> dict:
422+
def parse_line_profile_results(self, line_profiler_output_file: Path) -> dict[str, Any]:
426423
"""Parse line profiler output for Java.
427424
428425
Args:

codeflash/languages/registry.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ def _ensure_languages_registered() -> None:
5858
with contextlib.suppress(ImportError):
5959
from codeflash.languages.java import support as _ # noqa: F401
6060

61-
with contextlib.suppress(ImportError):
62-
from codeflash.languages.java import support as _ # noqa: F401
63-
6461
_languages_registered = True
6562

6663

0 commit comments

Comments
 (0)