Skip to content

Commit 83fabf5

Browse files
committed
wip
1 parent 1d48849 commit 83fabf5

File tree

7 files changed

+716
-3
lines changed

7 files changed

+716
-3
lines changed

codeflash/code_utils/code_extractor.py

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
from __future__ import annotations
33

44
import ast
5+
import json
6+
import subprocess
57
from itertools import chain
8+
from pathlib import Path
69
from typing import TYPE_CHECKING, Optional
710

811
import libcst as cst
@@ -14,12 +17,10 @@
1417
from codeflash.models.models import FunctionParent
1518

1619
if TYPE_CHECKING:
17-
from pathlib import Path
18-
1920
from libcst.helpers import ModuleNameAndPackage
2021

2122
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
22-
from codeflash.models.models import FunctionSource
23+
from codeflash.models.models import FunctionSource, ImpactMetrics
2324

2425

2526
class GlobalAssignmentCollector(cst.CSTVisitor):
@@ -748,3 +749,85 @@ def find_preexisting_objects(source_code: str) -> set[tuple[str, tuple[FunctionP
748749
if isinstance(cnode, (ast.FunctionDef, ast.AsyncFunctionDef)):
749750
preexisting_objects.add((cnode.name, (FunctionParent(node.name, "ClassDef"),)))
750751
return preexisting_objects
752+
753+
754+
def search_with_ripgrep(pattern: str, path: str = ".") -> dict[str, list[tuple[int, str]]]:
755+
"""Use ripgrep to search for a pattern in the repository.
756+
757+
Args:
758+
pattern: The pattern to search for
759+
path: The directory to search in (default: current directory)
760+
761+
Returns:
762+
Dictionary with filepaths as keys and list of (line_no, content) tuples as values
763+
764+
"""
765+
# Run ripgrep with JSON output for easier parsing
766+
# -n: Show line numbers
767+
# --json: Output in JSON format
768+
# --no-heading: Don't group matches by file
769+
path = str(Path.cwd())
770+
cmd = [
771+
"rg",
772+
"-n",
773+
"--json",
774+
pattern,
775+
path,
776+
"-g",
777+
"!/Users/aseemsaxena/Downloads/codeflash_dev/codeflash/code_to_optimize/tests/**",
778+
]
779+
print(" ".join(cmd))
780+
# Parse the JSON output
781+
matches_dict = {}
782+
try:
783+
result = subprocess.run(
784+
cmd,
785+
capture_output=True,
786+
text=True,
787+
check=False, # Don't raise exception on non-zero return
788+
)
789+
790+
if result.returncode not in [0, 1]: # 0 = matches found, 1 = no matches
791+
print(f"Error running ripgrep: {result.stderr}")
792+
return {}
793+
794+
for line in result.stdout.strip().split("\n"):
795+
if not line:
796+
continue
797+
798+
try:
799+
json_obj = json.loads(line)
800+
801+
# We're only interested in match objects
802+
if json_obj.get("type") == "match":
803+
data = json_obj.get("data", {})
804+
file_path = data.get("path", {}).get("text", "")
805+
line_number = data.get("line_number")
806+
line_content = data.get("lines", {}).get("text", "").rstrip("\n")
807+
808+
if file_path and line_number:
809+
if file_path not in matches_dict:
810+
matches_dict[file_path] = []
811+
matches_dict[file_path].append((line_number, line_content))
812+
813+
except json.JSONDecodeError:
814+
continue
815+
816+
except FileNotFoundError:
817+
print("Error: ripgrep (rg) is not installed or not in PATH")
818+
return {}
819+
except Exception as e:
820+
print(f"Unexpected error: {e}")
821+
return {}
822+
return matches_dict
823+
824+
825+
def get_opt_impact_metrics(file_path: Path, qualified_name: str, project_root: Path, tests_root: Path) -> ImpactMetrics:
826+
# grep for function / use rg (respects gitignore)
827+
# SAFE_GREP_EXECUTABLE command
828+
# ast visitor for occurances and loop occurances
829+
# radon lib for complexity metrics
830+
print(file_path, qualified_name, project_root, tests_root)
831+
832+
# grep windows alternative
833+
return 0

codeflash/code_utils/compat.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import shutil
23
import sys
34
import tempfile
45
from pathlib import Path
@@ -17,6 +18,7 @@ class Compat:
1718
LF: str = os.linesep
1819

1920
SAFE_SYS_EXECUTABLE: str = Path(sys.executable).as_posix()
21+
SAFE_GREP_EXECUTABLE: str = shutil.which("grep") # works even grep is aliased in the env
2022

2123
IS_POSIX: bool = os.name != "nt"
2224

@@ -45,3 +47,4 @@ def codeflash_cache_db(self) -> Path:
4547
LF = _compat.LF
4648
SAFE_SYS_EXECUTABLE = _compat.SAFE_SYS_EXECUTABLE
4749
IS_POSIX = _compat.IS_POSIX
50+
SAFE_GREP_EXECUTABLE = _compat.SAFE_GREP_EXECUTABLE

codeflash/models/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
from codeflash.verification.comparator import comparator
3232

3333

34+
@dataclass
35+
class ImpactMetrics:
36+
complexity_score: int
37+
occurances: int
38+
loop_occurances: int
39+
presence_of_decorators: bool
40+
41+
3442
@dataclass(frozen=True)
3543
class AIServiceRefinerRequest:
3644
optimization_id: str

codeflash/optimization/function_optimizer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from codeflash.benchmarking.utils import process_benchmark_data
2525
from codeflash.cli_cmds.console import code_print, console, logger, lsp_log, progress_bar
2626
from codeflash.code_utils import env_utils
27+
from codeflash.code_utils.code_extractor import get_opt_impact_metrics
2728
from codeflash.code_utils.code_replacer import (
2829
add_custom_marker_to_all_tests,
2930
modify_autouse_fixture,
@@ -1467,6 +1468,9 @@ def process_review(
14671468
except Exception as e:
14681469
logger.debug(f"optimization impact response failed, investigate {e}")
14691470
data["optimization_impact"] = opt_impact_response
1471+
data["impact_metrics"] = get_opt_impact_metrics(
1472+
self.project_root, self.test_cfg.tests_root
1473+
) # need module root, tests root only
14701474
if raise_pr and not staging_review:
14711475
data["git_remote"] = self.args.git_remote
14721476
check_create_pr(**data)

example_usage.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Example of using the ripgrep search script programmatically.
4+
"""
5+
6+
from ripgrep_search import search_with_ripgrep
7+
import json
8+
9+
# Search for any pattern you want
10+
pattern = "sorter" # Change this to any pattern you need
11+
results = search_with_ripgrep(pattern)
12+
13+
# Access the results as a dictionary
14+
print(f"Found matches in {len(results)} files")
15+
16+
# Iterate through the results
17+
for filepath, occurrences in results.items():
18+
print(f"\n{filepath}: {len(occurrences)} matches")
19+
for line_no, content in occurrences[:3]: # Show first 3 matches per file
20+
print(f" Line {line_no}: {content[:80]}...")
21+
22+
# Save results to a JSON file if needed
23+
with open("search_results.json", "w") as f:
24+
json.dump(results, f, indent=2)
25+
26+
# Or filter results for specific files
27+
python_files_only = {
28+
path: matches
29+
for path, matches in results.items()
30+
if path.endswith('.py')
31+
}
32+
33+
print(f"\nPython files with matches: {len(python_files_only)}")

0 commit comments

Comments
 (0)