Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ab500c1
Update codeflash.code-workspace
KRRT7 Sep 13, 2025
f98dae6
Update parse_test_output.py
KRRT7 Sep 16, 2025
96211b8
add tests_project_root to the wrappers
KRRT7 Sep 16, 2025
f872d5d
Update codeflash.code-workspace
KRRT7 Sep 16, 2025
ee1d386
Revert "add tests_project_root to the wrappers"
KRRT7 Sep 16, 2025
b858881
Update test_runner.py
KRRT7 Sep 16, 2025
a42c63a
Delete test_extract_test_context_from_frame.py
KRRT7 Sep 17, 2025
fadbdf7
better impl
KRRT7 Sep 17, 2025
1034ee8
Update parse_test_output.py
KRRT7 Sep 17, 2025
4ae0f93
calculate throughput
KRRT7 Sep 17, 2025
fb66ff5
add unit tests
KRRT7 Sep 17, 2025
2190963
fix wrapper validation test
KRRT7 Sep 17, 2025
2a3c975
fix linting
KRRT7 Sep 17, 2025
9c53948
cleanup
KRRT7 Sep 17, 2025
e5c4562
Update function_optimizer.py
KRRT7 Sep 17, 2025
04d31b5
optimized candidate result oo
KRRT7 Sep 17, 2025
8e516fc
critic
KRRT7 Sep 18, 2025
d7f9bbd
update tests again
KRRT7 Sep 18, 2025
58e5592
Update codeflash_wrap_decorator.py
KRRT7 Sep 18, 2025
a7a06ea
Update test_async_wrapper_sqlite_validation.py
KRRT7 Sep 22, 2025
d1b40d0
check.
KRRT7 Sep 22, 2025
a947ea0
go
KRRT7 Sep 22, 2025
2e0f38f
throughput
KRRT7 Sep 22, 2025
d3eefca
perfstdout
KRRT7 Sep 22, 2025
039c5ba
calculate throughtput for baseline
KRRT7 Sep 22, 2025
aba3dcb
Update function_optimizer.py
KRRT7 Sep 22, 2025
d3afd8a
Update critic.py
KRRT7 Sep 22, 2025
971470e
adjust critic
KRRT7 Sep 22, 2025
00a8981
update tests
KRRT7 Sep 22, 2025
1e4b3ba
pre-commit fixes
KRRT7 Sep 22, 2025
654055d
unbound local
KRRT7 Sep 22, 2025
540b8aa
Optimize AsyncCallInstrumenter.visit_ClassDef
codeflash-ai[bot] Sep 22, 2025
7aa30a8
Merge pull request #744 from codeflash-ai/codeflash/optimize-pr739-20…
KRRT7 Sep 22, 2025
fecdc94
Update .pre-commit-config.yaml
KRRT7 Sep 22, 2025
eccdeff
Update pre-commit.yaml
KRRT7 Sep 22, 2025
8b1b2be
Update .pre-commit-config.yaml
KRRT7 Sep 22, 2025
515b976
Delete pre-commit.yaml
KRRT7 Sep 22, 2025
30c8988
give throughput context to explanations
KRRT7 Sep 22, 2025
d8849a5
logger.debug
KRRT7 Sep 23, 2025
47384e2
add end to end test
KRRT7 Sep 23, 2025
43615bd
Create e2e-async.yaml
KRRT7 Sep 23, 2025
9aa34d9
Merge pull request #752 from codeflash-ai/end-to-end-test
KRRT7 Sep 23, 2025
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
6 changes: 5 additions & 1 deletion codeflash.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@
"request": "launch",
"program": "${workspaceFolder:codeflash}/codeflash/main.py",
"args": [
"--file", "src/async_examples/shocker.py", "--verbose"
"--file",
"src/async_examples/concurrency.py",
"--function",
"task",
"--verbose"
],
"cwd": "${input:chooseCwd}",
"console": "integratedTerminal",
Expand Down
196 changes: 16 additions & 180 deletions codeflash/code_utils/codeflash_wrap_decorator.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
from __future__ import annotations

import asyncio
import contextlib
import gc
import inspect
import os
import sqlite3
import time
from enum import Enum
from functools import wraps
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import TYPE_CHECKING, Any, Callable, TypeVar
from typing import Any, Callable, TypeVar

import dill as pickle

if TYPE_CHECKING:
from types import FrameType


class VerificationType(str, Enum): # moved from codeflash/verification/codeflash_capture.py
FUNCTION_CALL = (
Expand All @@ -36,185 +30,27 @@ def get_run_tmp_file(file_path: Path) -> Path: # moved from codeflash/code_util
return Path(get_run_tmp_file.tmpdir.name) / file_path


def _extract_class_name_tracer(frame_locals: dict[str, Any]) -> str | None:
try:
self_arg = frame_locals.get("self")
if self_arg is not None:
try:
return self_arg.__class__.__name__
except (AttributeError, Exception):
cls_arg = frame_locals.get("cls")
if cls_arg is not None:
with contextlib.suppress(AttributeError, Exception):
return cls_arg.__name__
else:
cls_arg = frame_locals.get("cls")
if cls_arg is not None:
with contextlib.suppress(AttributeError, Exception):
return cls_arg.__name__
except Exception:
return None
return None


def _get_module_name_cf_tracer(frame: FrameType | None) -> str:
try:
test_module = inspect.getmodule(frame)
except Exception:
test_module = None

if test_module is not None:
module_name = getattr(test_module, "__name__", None)
if module_name is not None:
return module_name

if frame is not None:
return frame.f_globals.get("__name__", "unknown_module")
return "unknown_module"


def extract_test_context_from_frame() -> tuple[str, str | None, str]:
frame = inspect.currentframe()
# optimize?
try:
frames_info = []
potential_tests = []

# First pass: collect all frame information
if frame is not None:
frame = frame.f_back

while frame is not None:
try:
function_name = frame.f_code.co_name
filename = frame.f_code.co_filename
filename_path = Path(filename)
frame_locals = frame.f_locals
test_module_name = _get_module_name_cf_tracer(frame)
class_name = _extract_class_name_tracer(frame_locals)

frames_info.append(
{
"function_name": function_name,
"filename_path": filename_path,
"frame_locals": frame_locals,
"test_module_name": test_module_name,
"class_name": class_name,
"frame": frame,
}
)

except Exception: # noqa: S112
continue

frame = frame.f_back

# Second pass: analyze frames with full context
test_class_candidates = []
for frame_info in frames_info:
function_name = frame_info["function_name"]
filename_path = frame_info["filename_path"]
frame_locals = frame_info["frame_locals"]
test_module_name = frame_info["test_module_name"]
class_name = frame_info["class_name"]
frame_obj = frame_info["frame"]

# Keep track of test classes
if class_name and (
class_name.startswith("Test") or class_name.endswith("Test") or "test" in class_name.lower()
):
test_class_candidates.append((class_name, test_module_name))

# Now process frames again looking for test functions with full candidates list
# Collect all test functions to prioritize outer ones over nested ones
test_functions = []
for frame_info in frames_info:
function_name = frame_info["function_name"]
filename_path = frame_info["filename_path"]
frame_locals = frame_info["frame_locals"]
test_module_name = frame_info["test_module_name"]
class_name = frame_info["class_name"]
frame_obj = frame_info["frame"]

# Collect test functions
if function_name.startswith("test_"):
test_class_name = class_name

# If no class found in current frame, check if we have any test class candidates
# Prefer the innermost (first) test class candidate which is more specific
if test_class_name is None and test_class_candidates:
test_class_name = test_class_candidates[0][0]

test_functions.append((test_module_name, test_class_name, function_name))

# Prioritize test functions with class context, then innermost
if test_functions:
# First prefer test functions with class context
for test_func in test_functions:
if test_func[1] is not None: # has class_name
return test_func
# If no test function has class context, return the outermost (most likely the actual test method)
return test_functions[-1]

# If no direct test functions found, look for other test patterns
for frame_info in frames_info:
function_name = frame_info["function_name"]
filename_path = frame_info["filename_path"]
frame_locals = frame_info["frame_locals"]
test_module_name = frame_info["test_module_name"]
class_name = frame_info["class_name"]
frame_obj = frame_info["frame"]

# Test file/module detection
if (
frame_obj.f_globals.get("__name__", "").startswith("test_")
or filename_path.stem.startswith("test_")
or "test" in filename_path.parts
):
if class_name and (
class_name.startswith("Test") or class_name.endswith("Test") or "test" in class_name.lower()
):
potential_tests.append((test_module_name, class_name, function_name))
elif "test" in test_module_name or filename_path.stem.startswith("test_"):
# For functions without class context, try to find the most recent test class
best_class = test_class_candidates[0][0] if test_class_candidates else None
potential_tests.append((test_module_name, best_class, function_name))

# Framework integration detection
if (
(
function_name in ["runTest", "_runTest", "run", "_testMethodName"]
or "pytest" in str(frame_obj.f_globals.get("__file__", ""))
or "unittest" in str(frame_obj.f_globals.get("__file__", ""))
)
and class_name
and (class_name.startswith("Test") or "test" in class_name.lower())
):
test_method = function_name
if "self" in frame_locals:
with contextlib.suppress(AttributeError, TypeError):
test_method = getattr(frame_locals["self"], "_testMethodName", function_name)
potential_tests.append((test_module_name, class_name, test_method))

if potential_tests:
for test_module, test_class, test_func in potential_tests:
if test_func.startswith("test_"):
return test_module, test_class, test_func
return potential_tests[0]

raise RuntimeError("No test function found in call stack")
finally:
del frame
def extract_test_context_from_env() -> tuple[str, str | None, str]:
test_module = os.environ["CODEFLASH_TEST_MODULE"]
test_class = os.environ.get("CODEFLASH_TEST_CLASS", None)
test_function = os.environ["CODEFLASH_TEST_FUNCTION"]

if test_module and test_function:
return (test_module, test_class if test_class else None, test_function)

raise RuntimeError(
"Test context environment variables not set - ensure tests are run through codeflash test runner"
)


def codeflash_behavior_async(func: F) -> F:
@wraps(func)
async def async_wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
loop = asyncio.get_running_loop()
function_name = func.__name__
line_id = f"{func.__name__}_{func.__code__.co_firstlineno}"
line_id = os.environ["CODEFLASH_CURRENT_LINE_ID"]
loop_index = int(os.environ["CODEFLASH_LOOP_INDEX"])
test_module_name, test_class_name, test_name = extract_test_context_from_frame()
test_module_name, test_class_name, test_name = extract_test_context_from_env()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in a line below why do we have a default of 0 - iteration = os.environ.get("CODEFLASH_TEST_ITERATION", "0")


test_id = f"{test_module_name}:{test_class_name}:{test_name}:{line_id}:{loop_index}"

Expand Down Expand Up @@ -288,10 +124,10 @@ def codeflash_performance_async(func: F) -> F:
async def async_wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
loop = asyncio.get_running_loop()
function_name = func.__name__
line_id = f"{func.__name__}_{func.__code__.co_firstlineno}"
line_id = os.environ["CODEFLASH_CURRENT_LINE_ID"]
loop_index = int(os.environ["CODEFLASH_LOOP_INDEX"])

test_module_name, test_class_name, test_name = extract_test_context_from_frame()
test_module_name, test_class_name, test_name = extract_test_context_from_env()

test_id = f"{test_module_name}:{test_class_name}:{test_name}:{line_id}:{loop_index}"

Expand Down
1 change: 1 addition & 0 deletions codeflash/code_utils/config_consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
MIN_TESTCASE_PASSED_THRESHOLD = 6
REPEAT_OPTIMIZATION_PROBABILITY = 0.1
DEFAULT_IMPORTANCE_THRESHOLD = 0.001
MIN_THROUGHPUT_IMPROVEMENT_THRESHOLD = 0.10 # 10% minimum improvement for async throughput
Loading
Loading