Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b058dee
perf: disable Sentry subprocess instrumentation and tracing
KRRT7 Feb 19, 2026
b19e1bd
perf: replace backtracking regexes with character-class patterns in p…
KRRT7 Feb 19, 2026
67ee734
perf: remove redundant project_root_path.resolve() from hot path
KRRT7 Feb 19, 2026
377083a
style: auto-fix ruff formatting in parse_test_output.py
github-actions[bot] Feb 19, 2026
486f6b8
fix: use disabled_integrations to exclude StdlibIntegration
KRRT7 Feb 19, 2026
8995279
style: add return type annotations to test methods
github-actions[bot] Feb 19, 2026
4742585
perf: remove remaining redundant .resolve() calls on pre-resolved paths
KRRT7 Feb 19, 2026
27937d4
Merge branch 'narrow-sentry-stdlib-integration' of https://github.com…
KRRT7 Feb 19, 2026
dc9c60a
perf: remove remaining redundant .resolve() calls on pre-resolved paths
KRRT7 Feb 19, 2026
ac096d9
style: auto-fix line length formatting in functions_to_optimize.py
github-actions[bot] Feb 19, 2026
e285908
fix: resolve tests_project_root at inject_profiling entry point
KRRT7 Feb 19, 2026
b3fd6bf
fix: resolve both sides of path comparisons for Windows 8.3 name cons…
KRRT7 Feb 19, 2026
a726b46
fix: guard against None definition_path from Jedi
KRRT7 Feb 19, 2026
5efa1ee
style: remove unused os import in code_context_extractor
github-actions[bot] Feb 19, 2026
74246e6
fix: resolve function_to_optimize.file_path for Windows 8.3 name cons…
github-actions[bot] Feb 19, 2026
3cdf710
fix: use dataclasses.replace() for frozen FunctionToOptimize path res…
github-actions[bot] Feb 19, 2026
8f65c9f
fix: resolve paths in revert_unused_helper_functions for Windows 8.3 …
github-actions[bot] Feb 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions codeflash/cli_cmds/init_javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def determine_js_package_manager(project_root: Path) -> JsPackageManager:
"""
# Search from project_root up to filesystem root for lock files
# This supports monorepo setups where lock file is at workspace root
current_dir = project_root.resolve()
current_dir = project_root
while current_dir != current_dir.parent:
if (current_dir / "bun.lockb").exists() or (current_dir / "bun.lock").exists():
return JsPackageManager.BUN
Expand Down Expand Up @@ -161,7 +161,7 @@ def find_node_modules_with_package(project_root: Path, package_name: str) -> Pat
Path to the node_modules directory containing the package, or None if not found.

"""
current_dir = project_root.resolve()
current_dir = project_root
while current_dir != current_dir.parent:
node_modules = current_dir / "node_modules"
if node_modules.exists():
Expand Down
2 changes: 1 addition & 1 deletion codeflash/code_utils/code_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def get_qualified_name(module_name: str, full_qualified_name: str) -> str:

def module_name_from_file_path(file_path: Path, project_root_path: Path, *, traverse_up: bool = False) -> str:
try:
relative_path = file_path.resolve().relative_to(project_root_path.resolve())
relative_path = file_path.resolve().relative_to(project_root_path)
Comment thread
KRRT7 marked this conversation as resolved.
Outdated
return relative_path.with_suffix("").as_posix().replace("/", ".")
except ValueError:
if traverse_up:
Expand Down
4 changes: 2 additions & 2 deletions codeflash/discovery/discover_unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ class TestFunction:
class TestsCache:
SCHEMA_VERSION = 1 # Increment this when schema changes

def __init__(self, project_root_path: str | Path) -> None:
self.project_root_path = Path(project_root_path).resolve().as_posix()
def __init__(self, project_root_path: Path) -> None:
self.project_root_path = project_root_path.as_posix()
self.connection = sqlite3.connect(codeflash_cache_db)
self.cur = self.connection.cursor()

Expand Down
8 changes: 6 additions & 2 deletions codeflash/discovery/functions_to_optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,7 @@ def filter_functions(
*,
disable_logs: bool = False,
) -> tuple[dict[Path, list[FunctionToOptimize]], int]:
resolved_project_root = project_root.resolve()
filtered_modified_functions: dict[str, list[FunctionToOptimize]] = {}
blocklist_funcs = get_blocklisted_functions()
logger.debug(f"Blocklisted functions: {blocklist_funcs}")
Expand Down Expand Up @@ -912,7 +913,7 @@ def is_test_file(file_path_normalized: str) -> bool:
lang_support = get_language_support(Path(file_path))
if lang_support.language == Language.PYTHON:
try:
ast.parse(f"import {module_name_from_file_path(Path(file_path), project_root)}")
ast.parse(f"import {module_name_from_file_path(Path(file_path), resolved_project_root)}")
except SyntaxError:
malformed_paths_count += 1
continue
Expand All @@ -934,7 +935,10 @@ def is_test_file(file_path_normalized: str) -> bool:
if previous_checkpoint_functions:
functions_tmp = []
for function in _functions:
if function.qualified_name_with_modules_from_root(project_root) in previous_checkpoint_functions:
if (
function.qualified_name_with_modules_from_root(resolved_project_root)
in previous_checkpoint_functions
):
previous_checkpoint_functions_removed_count += 1
continue
functions_tmp.append(function)
Expand Down
3 changes: 1 addition & 2 deletions codeflash/languages/javascript/import_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ def __init__(self, project_root: Path) -> None:
project_root: Root directory of the project.

"""
# Resolve to real path to handle macOS symlinks like /var -> /private/var
self.project_root = project_root.resolve()
self.project_root = project_root
self._resolution_cache: dict[tuple[Path, str], Path | None] = {}

def resolve_import(self, import_info: ImportInfo, source_file: Path) -> ResolvedImport | None:
Expand Down
4 changes: 2 additions & 2 deletions codeflash/optimization/function_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ def __init__(
args: Namespace | None = None,
replay_tests_dir: Path | None = None,
) -> None:
self.project_root = test_cfg.project_root_path
self.project_root = test_cfg.project_root_path.resolve()
self.test_cfg = test_cfg
self.aiservice_client = aiservice_client if aiservice_client else AiServiceClient()
self.function_to_optimize = function_to_optimize
Expand Down Expand Up @@ -1451,7 +1451,7 @@ def reformat_code_and_helpers(
optimized_code = ""
if optimized_context is not None:
file_to_code_context = optimized_context.file_to_path()
optimized_code = file_to_code_context.get(str(path.relative_to(self.project_root)), "")
optimized_code = file_to_code_context.get(str(path.resolve().relative_to(self.project_root)), "")

new_code = format_code(
self.args.formatter_cmds, path, optimized_code=optimized_code, check_diff=True, exit_on_failure=False
Expand Down
4 changes: 2 additions & 2 deletions codeflash/result/create_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ def existing_tests_source_for(
tests_dir_name = test_cfg.tests_project_rootdir.name
if file_path.startswith((tests_dir_name + os.sep, tests_dir_name + "/")):
# Module path includes "tests." - use project root parent
instrumented_abs_path = (test_cfg.tests_project_rootdir.parent / file_path).resolve()
instrumented_abs_path = test_cfg.tests_project_rootdir.parent / file_path
else:
# Module path doesn't include tests dir - use tests root directly
instrumented_abs_path = (test_cfg.tests_project_rootdir / file_path).resolve()
instrumented_abs_path = test_cfg.tests_project_rootdir / file_path
logger.debug(f"[PR-DEBUG] Looking up: {instrumented_abs_path}")
logger.debug(f"[PR-DEBUG] Available keys: {list(instrumented_to_original.keys())[:3]}")
# Try to map instrumented path to original path
Expand Down
11 changes: 4 additions & 7 deletions codeflash/telemetry/sentry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import sentry_sdk
from sentry_sdk.integrations.logging import LoggingIntegration
from sentry_sdk.integrations.stdlib import StdlibIntegration


def init_sentry(*, enabled: bool = False, exclude_errors: bool = False) -> None:
Expand All @@ -16,12 +17,8 @@ def init_sentry(*, enabled: bool = False, exclude_errors: bool = False) -> None:
sentry_sdk.init(
dsn="https://4b9a1902f9361b48c04376df6483bc96@o4506833230561280.ingest.sentry.io/4506833262477312",
integrations=[sentry_logging],
Comment thread
KRRT7 marked this conversation as resolved.
# Set traces_sample_rate to 1.0 to capture 100%
# of transactions for performance monitoring.
traces_sample_rate=1.0,
# Set profiles_sample_rate to 1.0 to profile 100%
# of sampled transactions.
# We recommend adjusting this value in production.
profiles_sample_rate=1.0,
disabled_integrations=[StdlibIntegration],
traces_sample_rate=0,
profiles_sample_rate=0,
ignore_errors=[KeyboardInterrupt],
)
23 changes: 19 additions & 4 deletions codeflash/verification/parse_test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,24 @@ def parse_func(file_path: Path) -> XMLParser:
return parse(file_path, xml_parser)


matches_re_start = re.compile(r"!\$######(.*?):(.*?)([^\.:]*?):(.*?):(.*?):(.*?)######\$!\n")
matches_re_end = re.compile(r"!######(.*?):(.*?)([^\.:]*?):(.*?):(.*?):(.*?)######!")
matches_re_start = re.compile(
r"!\$######([^:]*)" # group 1: module path
r":((?:[^:.]*\.)*)" # group 2: class prefix with trailing dot, or empty
r"([^.:]*)" # group 3: test function name
r":([^:]*)" # group 4: function being tested
r":([^:]*)" # group 5: loop index
r":([^#]*)" # group 6: iteration id
r"######\$!\n"
)
matches_re_end = re.compile(
r"!######([^:]*)" # group 1: module path
r":((?:[^:.]*\.)*)" # group 2: class prefix with trailing dot, or empty
r"([^.:]*)" # group 3: test function name
r":([^:]*)" # group 4: function being tested
r":([^:]*)" # group 5: loop index
r":([^#]*)" # group 6: iteration_id or iteration_id:runtime
r"######!"
)


start_pattern = re.compile(r"!\$######([^:]*):([^:]*):([^:]*):([^:]*):([^:]+)######\$!")
Expand Down Expand Up @@ -893,7 +909,6 @@ def merge_test_results(
return merged_test_results


FAILURES_HEADER_RE = re.compile(r"=+ FAILURES =+")
TEST_HEADER_RE = re.compile(r"_{3,}\s*(.*?)\s*_{3,}$")


Expand All @@ -903,7 +918,7 @@ def parse_test_failures_from_stdout(stdout: str) -> dict[str, str]:
start = end = None

for i, line in enumerate(lines):
if FAILURES_HEADER_RE.search(line.strip()):
if "= FAILURES =" in line:
start = i
break

Expand Down
4 changes: 4 additions & 0 deletions codeflash/verification/verification_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ class TestConfig:
_language: Optional[str] = None # Language identifier for multi-language support
js_project_root: Optional[Path] = None # JavaScript project root (directory containing package.json)

def __post_init__(self) -> None:
self.project_root_path = self.project_root_path.resolve()
self.tests_project_rootdir = self.tests_project_rootdir.resolve()
Comment thread
KRRT7 marked this conversation as resolved.

@property
def test_framework(self) -> str:
"""Returns the appropriate test framework based on language.
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ warn_unreachable = true
install_types = true
plugins = ["pydantic.mypy"]

exclude = ["tests/", "code_to_optimize/", "pie_test_set/", "experiments/"]

[[tool.mypy.overrides]]
module = ["jedi", "jedi.api.classes", "inquirer", "inquirer.themes", "numba"]
ignore_missing_imports = true
Expand Down
189 changes: 189 additions & 0 deletions tests/test_parse_test_output_regex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
"""Tests for the regex patterns and string matching in parse_test_output.py."""

from codeflash.verification.parse_test_output import (
matches_re_end,
matches_re_start,
parse_test_failures_from_stdout,
)


# --- matches_re_start tests ---


class TestMatchesReStart:
def test_simple_no_class(self) -> None:
s = "!$######tests.test_foo:test_bar:target_func:1:abc######$!\n"
m = matches_re_start.search(s)
assert m is not None
assert m.groups() == ("tests.test_foo", "", "test_bar", "target_func", "1", "abc")

def test_with_class(self) -> None:
s = "!$######tests.test_foo:MyClass.test_bar:target_func:1:abc######$!\n"
m = matches_re_start.search(s)
assert m is not None
assert m.groups() == ("tests.test_foo", "MyClass.", "test_bar", "target_func", "1", "abc")

def test_nested_class(self) -> None:
s = "!$######a.b.c:A.B.test_x:func:3:id123######$!\n"
m = matches_re_start.search(s)
assert m is not None
assert m.groups() == ("a.b.c", "A.B.", "test_x", "func", "3", "id123")

def test_empty_class_and_function(self) -> None:
s = "!$######mod::func:0:iter######$!\n"
m = matches_re_start.search(s)
assert m is not None
assert m.groups() == ("mod", "", "", "func", "0", "iter")

def test_embedded_in_stdout(self) -> None:
s = "some output\n!$######mod:test_fn:f:1:x######$!\nmore output\n"
m = matches_re_start.search(s)
assert m is not None
assert m.groups() == ("mod", "", "test_fn", "f", "1", "x")

def test_multiple_matches(self) -> None:
s = (
"!$######m1:C1.fn1:t1:1:a######$!\n"
"!$######m2:fn2:t2:2:b######$!\n"
)
matches = list(matches_re_start.finditer(s))
assert len(matches) == 2
assert matches[0].groups() == ("m1", "C1.", "fn1", "t1", "1", "a")
assert matches[1].groups() == ("m2", "", "fn2", "t2", "2", "b")

def test_no_match_without_newline(self) -> None:
s = "!$######mod:test_fn:f:1:x######$!"
m = matches_re_start.search(s)
assert m is None

def test_dots_in_module_path(self) -> None:
s = "!$######a.b.c.d.e:test_fn:f:1:x######$!\n"
m = matches_re_start.search(s)
assert m is not None
assert m.group(1) == "a.b.c.d.e"


# --- matches_re_end tests ---


class TestMatchesReEnd:
def test_simple_no_class_with_runtime(self) -> None:
s = "!######tests.test_foo:test_bar:target_func:1:abc:12345######!"
m = matches_re_end.search(s)
assert m is not None
assert m.groups() == ("tests.test_foo", "", "test_bar", "target_func", "1", "abc:12345")

def test_with_class_no_runtime(self) -> None:
s = "!######tests.test_foo:MyClass.test_bar:target_func:1:abc######!"
m = matches_re_end.search(s)
assert m is not None
assert m.groups() == ("tests.test_foo", "MyClass.", "test_bar", "target_func", "1", "abc")

def test_nested_class_with_runtime(self) -> None:
s = "!######mod:A.B.test_x:func:3:id123:99999######!"
m = matches_re_end.search(s)
assert m is not None
assert m.groups() == ("mod", "A.B.", "test_x", "func", "3", "id123:99999")

def test_runtime_colon_preserved_in_group6(self) -> None:
"""Group 6 must capture 'iteration_id:runtime' as a single string (colon included)."""
s = "!######m:fn:f:1:iter42:98765######!"
m = matches_re_end.search(s)
assert m is not None
assert m.group(6) == "iter42:98765"

def test_embedded_in_stdout(self) -> None:
s = "captured output\n!######mod:test_fn:f:1:x:500######!\nmore"
m = matches_re_end.search(s)
assert m is not None
assert m.groups() == ("mod", "", "test_fn", "f", "1", "x:500")


# --- Start/End pairing (simulates parse_test_xml matching logic) ---


class TestStartEndPairing:
def test_paired_markers(self) -> None:
stdout = (
"!$######mod:Class.test_fn:func:1:iter1######$!\n"
"test output here\n"
"!######mod:Class.test_fn:func:1:iter1:54321######!"
)
starts = list(matches_re_start.finditer(stdout))
ends = {}
for match in matches_re_end.finditer(stdout):
groups = match.groups()
g5 = groups[5]
colon_pos = g5.find(":")
if colon_pos != -1:
key = groups[:5] + (g5[:colon_pos],)
else:
key = groups
ends[key] = match

assert len(starts) == 1
assert len(ends) == 1
# Start and end should pair on the first 5 groups + iteration_id
start_groups = starts[0].groups()
assert start_groups in ends


# --- parse_test_failures_from_stdout tests ---


class TestParseTestFailuresHeader:
def test_standard_pytest_header(self) -> None:
stdout = (
"..F.\n"
"=================================== FAILURES ===================================\n"
"_______ test_foo _______\n"
"\n"
" def test_foo():\n"
"> assert False\n"
"E AssertionError\n"
"\n"
"test.py:3: AssertionError\n"
"=========================== short test summary info ============================\n"
"FAILED test.py::test_foo\n"
)
result = parse_test_failures_from_stdout(stdout)
assert "test_foo" in result

def test_minimal_equals(self) -> None:
"""Even a short '= FAILURES =' header should be detected."""
stdout = (
"= FAILURES =\n"
"_______ test_bar _______\n"
"\n"
" assert False\n"
"\n"
"test.py:1: AssertionError\n"
"= short test summary info =\n"
)
result = parse_test_failures_from_stdout(stdout)
assert "test_bar" in result

def test_no_failures_section(self) -> None:
stdout = "....\n4 passed in 0.1s\n"
result = parse_test_failures_from_stdout(stdout)
assert result == {}

def test_word_failures_without_equals_is_not_matched(self) -> None:
"""'FAILURES' without surrounding '=' signs should not trigger the header detection."""
stdout = (
"FAILURES detected in module\n"
"_______ test_baz _______\n"
"\n"
" assert False\n"
)
result = parse_test_failures_from_stdout(stdout)
assert result == {}

def test_failures_in_test_output_not_matched(self) -> None:
"""A test printing 'FAILURES' (no = signs) should not trigger header detection."""
stdout = (
"Testing FAILURES handling\n"
"All good\n"
)
result = parse_test_failures_from_stdout(stdout)
assert result == {}
Loading