-
Notifications
You must be signed in to change notification settings - Fork 22
enable async function optimization #769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add is_async parameter to AiServiceClient.optimize_python_code method - Pass is_async flag to both optimization and test generation endpoints - Update optimization pipeline to pass is_async from FunctionToOptimize - Remove async function blocking check in function optimizer - Remove unused has_any_async_functions import This enables async functions to be properly processed by the optimization pipeline and sent to the AI service with the correct async context.
- Added comprehensive async test instrumentation (AsyncCallInstrumenter class) - Implemented async decorator functions (add_async_decorator_to_function, instrument_source_module_with_async_decorators) - Added async wrapper decorators (codeflash_behavior_async, codeflash_performance_async) - Updated edit_generated_tests.py to handle AsyncFunctionDef nodes in test parsing - Updated coverage_utils.py to include async functions in coverage analysis
- Add async throughput fields to Explanation dataclass - Implement throughput-based performance improvement calculation - Add MIN_THROUGHPUT_IMPROVEMENT_THRESHOLD configuration constant - Update explanation logic to prefer throughput metrics for async functions - Restore LSP compatibility with conditional test result display
in my case, i've started using symlinks a bit more often, and the current impl causes issues, we need to resolve the symlinked path too.
PR Reviewer Guide 🔍(Review updated until commit 6795a5c)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 6795a5c
Previous suggestionsSuggestions up to commit 4325416
|
⚡️ Codeflash found optimizations for this PR📄 24% (0.24x) speedup for
|
⚡️ Codeflash found optimizations for this PR📄 30% (0.30x) speedup for
|
The optimization achieves a 22% speedup by eliminating redundant regex compilation and reducing unnecessary string operations. **Key optimizations:** 1. **Pre-compiled regex patterns**: The original code compiled the same regex pattern multiple times (3,114 compilations taking 43.4% of total time). The optimized version compiles each pattern only once upfront using `_compile_function_patterns()`, moving this expensive operation outside the nested loops. 2. **Efficient string manipulation**: Instead of using `re.sub()` which searches the entire string again, the optimized version uses `finditer()` to get match positions directly, then performs string slicing (`source[:start] + source[end:]`) to remove matched functions. This avoids the overhead of regex substitution. 3. **Early termination**: After finding and removing a function match, the code breaks from the inner loop since only one match per function is expected, preventing unnecessary continued iteration. **Performance impact by test case:** - The optimizations are most effective for scenarios with multiple test functions to remove across multiple generated tests (the typical use case) - For edge cases like empty test lists, there's minimal overhead from pre-compilation but no significant benefit - The approach maintains correct behavior for decorated functions (skipping `@pytest.mark.parametrize` functions as intended) The line profiler shows the regex compilation time dropped from 43.4% to being absorbed into the 89.8% upfront compilation cost, while the substitution overhead (51.7% in original) is eliminated entirely.
⚡️ Codeflash found optimizations for this PR📄 23% (0.23x) speedup for
|
…25-09-27T01.33.17 ⚡️ Speed up function `remove_functions_from_generated_tests` by 23% in PR #769 (`clean-async-branch`)
|
This PR is now faster! 🚀 @KRRT7 accepted my optimizations from: |
The optimized code achieves a **123% speedup** by replacing expensive AST traversal operations with more efficient alternatives: **Key Optimizations:** 1. **Decorator Search Optimization**: Replaced the `any()` generator expression with a simple loop that breaks early when finding `timeout_decorator.timeout`. This avoids unnecessary attribute lookups and iterations through the decorator list, especially beneficial when the decorator is found early or when there are many decorators. 2. **AST Traversal Replacement**: The most significant optimization replaces `ast.walk(stmt)` with a manual stack-based depth-first search in `_optimized_instrument_statement()`. The original `ast.walk()` creates a list of every node in the AST subtree, which is memory-intensive and includes many irrelevant nodes. The optimized version: - Uses a stack to traverse nodes manually - Only explores child nodes via `_fields` attribute access - Immediately returns when finding an `ast.Await` node that matches criteria - Avoids creating intermediate collections **Performance Impact by Test Case:** - **Large-scale tests** see the biggest improvements (125-129% faster) because they have many statements to traverse - **Nested structures** benefit significantly (57-93% faster) as the optimization avoids deep, unnecessary traversals - **Simple test cases** still see 29-48% improvements from the decorator optimization - **Functions with many await calls** show excellent scaling (123-127% faster) due to reduced per-statement traversal costs The line profiler shows the critical bottleneck was in `_instrument_statement()` (96.4% of time originally), which is now reduced to 93.3% but with much lower absolute time, demonstrating the effectiveness of the AST traversal optimization.
⚡️ Codeflash found optimizations for this PR📄 123% (1.23x) speedup for
|
⚡️ Codeflash found optimizations for this PR📄 39% (0.39x) speedup for
|
…25-09-27T02.50.03 ⚡️ Speed up method `AsyncCallInstrumenter.visit_AsyncFunctionDef` by 123% in PR #769 (`clean-async-branch`)
|
This PR is now faster! 🚀 @KRRT7 accepted my optimizations from: |
| if ( | ||
| isinstance(item, ast.FunctionDef) | ||
| and item.name.startswith("test_") | ||
| and not any( | ||
| isinstance(d, ast.Call) | ||
| and isinstance(d.func, ast.Name) | ||
| and d.func.id == "timeout_decorator.timeout" | ||
| for d in item.decorator_list | ||
| ) | ||
| ): | ||
| item.decorator_list.append(timeout_decorator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚡️Codeflash found 10% (0.10x) speedup for AsyncCallInstrumenter.visit_ClassDef in codeflash/code_utils/instrument_existing_tests.py
⏱️ Runtime : 4.11 milliseconds → 3.72 milliseconds (best of 216 runs)
📝 Explanation and details
The optimization replaces the computationally expensive any() generator expression with an explicit loop and early break pattern. In the original code, any() creates a generator that evaluates a complex condition for each decorator in item.decorator_list, even when an early match is found. The optimized version uses a simple flag-based approach with has_timeout_decorator = False and an explicit loop that breaks immediately upon finding the target decorator.
This change eliminates the overhead of:
- Generator function creation and iteration protocol
- Repeated evaluation of the complex isinstance() chain for all decorators when early matches occur
- The
any()builtin call overhead
The optimization is particularly effective for test cases with many test methods (13.5% speedup on 500 test functions) because it reduces the per-method decorator checking cost. For smaller classes, the speedup is more modest (5-11%) but still consistent. The explicit loop pattern allows the CPU to exit the decorator search as soon as a timeout decorator is found, rather than continuing to evaluate the generator expression.
✅ Correctness verification report:
| Test | Status |
|---|---|
| ⚙️ Existing Unit Tests | 🔘 None Found |
| 🌀 Generated Regression Tests | ✅ 50 Passed |
| ⏪ Replay Tests | 🔘 None Found |
| 🔎 Concolic Coverage Tests | 🔘 None Found |
| 📊 Tests Coverage | 100.0% |
🌀 Generated Regression Tests and Runtime
from __future__ import annotations
import ast
# imports
import pytest # used for our unit tests
from codeflash.code_utils.instrument_existing_tests import \
AsyncCallInstrumenter
# Dummy classes for dependencies
class FunctionToOptimize:
def __init__(self, function_name, parents=None, top_level_parent_name=None):
self.function_name = function_name
self.parents = parents or []
self.top_level_parent_name = top_level_parent_name
class CodePosition:
pass
class TestingMode:
BEHAVIOR = "behavior"
OTHER = "other"
from codeflash.code_utils.instrument_existing_tests import \
AsyncCallInstrumenter
# Helper to parse code and return AST
def parse_classdef(code: str) -> ast.ClassDef:
mod = ast.parse(code)
for node in mod.body:
if isinstance(node, ast.ClassDef):
return node
raise ValueError("No ClassDef found in code")
# Helper to check if a function has the timeout decorator
def has_timeout_decorator(func: ast.FunctionDef) -> bool:
for dec in func.decorator_list:
if (
isinstance(dec, ast.Call)
and isinstance(dec.func, ast.Name)
and dec.func.id == "timeout_decorator.timeout"
):
return True
return False
# ---------------- BASIC TEST CASES ----------------
def test_no_test_methods_unittest():
# Class with no test methods: nothing should be decorated
code = """
class MyTestCase:
def helper(self): pass
def not_a_test(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 10.2μs -> 10.2μs (0.098% slower)
# No function should have the timeout decorator
for item in result.body:
if isinstance(item, ast.FunctionDef):
pass
def test_single_test_method_unittest():
# Class with one test method: should be decorated
code = """
class MyTestCase:
def test_simple(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 12.2μs -> 11.6μs (5.90% faster)
# The test_simple method should have the timeout decorator
for item in result.body:
if isinstance(item, ast.FunctionDef) and item.name == "test_simple":
pass
def test_multiple_test_methods_unittest():
# Class with multiple test methods: all should be decorated
code = """
class MyTestCase:
def test_one(self): pass
def test_two(self): pass
def helper(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 15.2μs -> 14.2μs (7.18% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef):
if item.name.startswith("test_"):
pass
else:
pass
def test_non_unittest_framework():
# Should not decorate anything if not using unittest
code = """
class MyTestCase:
def test_one(self): pass
def test_two(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"pytest", # not unittest
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 9.34μs -> 9.33μs (0.096% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef):
pass
def test_already_has_timeout_decorator():
# Should not add duplicate timeout decorator
code = """
class MyTestCase:
@timeout_decorator.timeout(15)
def test_one(self): pass
def test_two(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 14.9μs -> 13.6μs (9.52% faster)
found = 0
for item in result.body:
if isinstance(item, ast.FunctionDef):
if item.name == "test_one":
found += 1
elif item.name == "test_two":
found += 1
# ---------------- EDGE TEST CASES ----------------
def test_decorator_list_is_empty():
# Handles function with no decorators
code = """
class MyTestCase:
def test_empty_decorator(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 11.3μs -> 10.6μs (7.12% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef) and item.name == "test_empty_decorator":
pass
def test_non_function_items_in_class():
# Handles classes with non-function items
code = """
class MyTestCase:
x = 5
def test_x(self): pass
@property
def y(self): return 10
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 23.0μs -> 22.0μs (4.69% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef):
if item.name == "test_x":
pass
elif item.name == "y":
pass
def test_function_name_edge_cases():
# Only functions starting with test_ get decorated
code = """
class MyTestCase:
def test(self): pass
def testCase(self): pass
def test_abc(self): pass
def test123(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 14.3μs -> 13.4μs (6.51% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef):
if item.name == "test_abc":
pass
else:
pass
def test_decorator_is_not_call():
# Handles decorators that are not ast.Call
code = """
class MyTestCase:
@staticmethod
def test_static(self): pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 11.9μs -> 10.8μs (9.88% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef) and item.name == "test_static":
# Should have both staticmethod and timeout_decorator
found_static = any(isinstance(dec, ast.Name) and dec.id == "staticmethod" for dec in item.decorator_list)
def test_empty_class_body():
# Class with no body should not raise
code = """
class MyTestCase:
pass
"""
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
# Should not raise
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 8.12μs -> 8.03μs (1.12% faster)
# ---------------- LARGE SCALE TEST CASES ----------------
def test_large_number_of_test_methods():
# Large class with many test methods
N = 500
code = "class BigTestCase:\n" + "\n".join(
f" def test_{i}(self): pass" for i in range(N)
)
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 954μs -> 840μs (13.6% faster)
count = 0
for item in result.body:
if isinstance(item, ast.FunctionDef):
count += 1
def test_large_class_mixed_methods():
# Large class with mixed test and non-test methods
N = 250
code = "class MixedTestCase:\n" + "\n".join(
f" def test_{i}(self): pass" for i in range(N)
) + "\n" + "\n".join(
f" def helper_{i}(self): pass" for i in range(N)
)
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"unittest",
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 641μs -> 569μs (12.5% faster)
test_count = helper_count = 0
for item in result.body:
if isinstance(item, ast.FunctionDef):
if item.name.startswith("test_"):
test_count += 1
else:
helper_count += 1
def test_large_non_unittest():
# Large class with unittest-like methods but non-unittest framework
N = 300
code = "class BigTestCase:\n" + "\n".join(
f" def test_{i}(self): pass" for i in range(N)
)
classdef = parse_classdef(code)
instrumenter = AsyncCallInstrumenter(
FunctionToOptimize("foo"),
"mod.py",
"pytest", # not unittest
[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); result = codeflash_output # 367μs -> 356μs (3.08% faster)
for item in result.body:
if isinstance(item, ast.FunctionDef):
pass
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from __future__ import annotations
import ast
# imports
import pytest # used for our unit tests
from codeflash.code_utils.instrument_existing_tests import \
AsyncCallInstrumenter
class DummyFunction:
def __init__(self, function_name, parents=None, top_level_parent_name=None):
self.function_name = function_name
self.parents = parents or []
self.top_level_parent_name = top_level_parent_name
# Helper to parse code and get ClassDef node
def get_classdef_node(source: str) -> ast.ClassDef:
tree = ast.parse(source)
for node in tree.body:
if isinstance(node, ast.ClassDef):
return node
raise ValueError("No ClassDef found in source")
# Helper to check if a functiondef has the timeout decorator
def has_timeout_decorator(funcdef: ast.FunctionDef) -> bool:
for d in funcdef.decorator_list:
if (
isinstance(d, ast.Call)
and isinstance(d.func, ast.Name)
and d.func.id == "timeout_decorator.timeout"
and len(d.args) == 1
and isinstance(d.args[0], ast.Constant)
and d.args[0].value == 15
):
return True
return False
# Basic Test Cases
def test_unittest_adds_timeout_to_test_functions():
# Test that the decorator is added to test functions for unittest
src = '''
class TestFoo:
def test_one(self): pass
def test_two(self): pass
def helper(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_one"),
module_path="foo.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 16.1μs -> 15.1μs (6.38% faster)
test_funcs = [item for item in new_node.body if isinstance(item, ast.FunctionDef)]
# Only test_ functions should get the decorator
for func in test_funcs:
if func.name.startswith("test_"):
pass
else:
pass
def test_pytest_does_not_add_timeout():
# Test that no decorator is added for pytest
src = '''
class TestBar:
def test_a(self): pass
def test_b(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_a"),
module_path="bar.py",
test_framework="pytest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 9.48μs -> 9.42μs (0.637% faster)
for func in [item for item in new_node.body if isinstance(item, ast.FunctionDef)]:
pass
def test_no_test_functions():
# Class with no test_ functions should not get any decorator
src = '''
class HelperClass:
def foo(self): pass
def bar(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("foo"),
module_path="helper.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 9.62μs -> 9.32μs (3.22% faster)
for func in [item for item in new_node.body if isinstance(item, ast.FunctionDef)]:
pass
def test_existing_timeout_decorator_not_duplicated():
# If timeout_decorator.timeout is already present, do not duplicate
src = '''
class TestDup:
@timeout_decorator.timeout(15)
def test_dup(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_dup"),
module_path="dup.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 12.1μs -> 11.1μs (8.65% faster)
func = [item for item in new_node.body if isinstance(item, ast.FunctionDef)][0]
# Edge Test Cases
def test_class_with_non_function_body_items():
# Class with assignments and other nodes in body
src = '''
class TestEdge:
x = 1
def test_func(self): pass
y = 2
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_func"),
module_path="edge.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 28.1μs -> 26.7μs (5.18% faster)
# Only test_func should get the decorator
for item in new_node.body:
if isinstance(item, ast.FunctionDef):
if item.name == "test_func":
pass
else:
pass
else:
pass
def test_class_with_decorators_on_test_function():
# Test function with other decorators should still get timeout_decorator
src = '''
class TestDecorators:
@other_decorator
def test_deco(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_deco"),
module_path="deco.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 12.1μs -> 10.9μs (11.1% faster)
func = [item for item in new_node.body if isinstance(item, ast.FunctionDef)][0]
def test_class_with_inherited_test_functions():
# Class with test functions that are inherited (should not be touched)
src = '''
class Base:
def test_base(self): pass
class Derived(Base):
def test_derived(self): pass
'''
tree = ast.parse(src)
# Only Derived should be instrumented
derived = [n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == "Derived"][0]
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_derived"),
module_path="inherit.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(derived); new_node = codeflash_output # 14.9μs -> 13.7μs (8.64% faster)
func = [item for item in new_node.body if isinstance(item, ast.FunctionDef)][0]
def test_class_with_test_function_named_exactly_test():
# Function named 'test' (not 'test_') should not get decorator
src = '''
class TestExact:
def test(self): pass
def test_one(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_one"),
module_path="exact.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 12.6μs -> 11.8μs (6.43% faster)
for func in [item for item in new_node.body if isinstance(item, ast.FunctionDef)]:
if func.name == "test_one":
pass
else:
pass
def test_class_with_async_test_functions():
# Async test functions should also be instrumented
src = '''
class TestAsync:
async def test_async(self): pass
'''
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_async"),
module_path="async.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 12.2μs -> 12.1μs (0.577% faster)
func = [item for item in new_node.body if isinstance(item, ast.AsyncFunctionDef)][0]
# Large Scale Test Cases
def test_large_number_of_test_functions():
# Class with many test_ functions
NUM_FUNCS = 500
src_lines = ["class TestLarge:"]
for i in range(NUM_FUNCS):
src_lines.append(f" def test_func_{i}(self): pass")
src = "\n".join(src_lines)
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_func_0"),
module_path="large.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 959μs -> 845μs (13.5% faster)
test_funcs = [item for item in new_node.body if isinstance(item, ast.FunctionDef)]
for func in test_funcs:
pass
def test_large_number_of_non_test_functions():
# Class with many non-test functions
NUM_FUNCS = 500
src_lines = ["class HelperLarge:"]
for i in range(NUM_FUNCS):
src_lines.append(f" def helper_func_{i}(self): pass")
src = "\n".join(src_lines)
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("helper_func_0"),
module_path="largehelper.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 299μs -> 294μs (1.85% faster)
helper_funcs = [item for item in new_node.body if isinstance(item, ast.FunctionDef)]
for func in helper_funcs:
pass
def test_large_mixed_class():
# Class with both test_ and non-test functions
NUM_TEST_FUNCS = 250
NUM_HELPER_FUNCS = 250
src_lines = ["class MixedLarge:"]
for i in range(NUM_TEST_FUNCS):
src_lines.append(f" def test_func_{i}(self): pass")
for i in range(NUM_HELPER_FUNCS):
src_lines.append(f" def helper_func_{i}(self): pass")
src = "\n".join(src_lines)
classdef = get_classdef_node(src)
instrumenter = AsyncCallInstrumenter(
DummyFunction("test_func_0"),
module_path="mixedlarge.py",
test_framework="unittest",
call_positions=[],
)
codeflash_output = instrumenter.visit_ClassDef(classdef); new_node = codeflash_output # 631μs -> 573μs (10.1% faster)
funcs = [item for item in new_node.body if isinstance(item, ast.FunctionDef)]
for func in funcs:
if func.name.startswith("test_"):
pass
else:
pass
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.To test or edit this optimization locally git merge codeflash/optimize-pr769-2025-09-29T21.00.36
Click to see suggested changes
| if ( | |
| isinstance(item, ast.FunctionDef) | |
| and item.name.startswith("test_") | |
| and not any( | |
| isinstance(d, ast.Call) | |
| and isinstance(d.func, ast.Name) | |
| and d.func.id == "timeout_decorator.timeout" | |
| for d in item.decorator_list | |
| ) | |
| ): | |
| item.decorator_list.append(timeout_decorator) | |
| if isinstance(item, ast.FunctionDef) and item.name.startswith("test_"): | |
| has_timeout_decorator = False | |
| for d in item.decorator_list: | |
| if ( | |
| isinstance(d, ast.Call) | |
| and isinstance(d.func, ast.Name) | |
| and d.func.id == "timeout_decorator.timeout" | |
| ): | |
| has_timeout_decorator = True | |
| break | |
| if not has_timeout_decorator: | |
| item.decorator_list.append(timeout_decorator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - but I'll merge this in later.
User description
less messy
PR Type
Enhancement, Tests
Description
Add comprehensive async test suite
Enable async optimization pipeline end-to-end
Throughput-aware performance selection logic
Fix coverage utils empty-data handling
Diagram Walkthrough
File Walkthrough
3 files
End-to-end async run/parse tests and behaviorsAsync decorator injection and profiling testsAsync helpers detection and revert scenarios2 files
Async-aware pipeline, benchmarking, throughput, explanationsAdd throughput gain and async-aware speedup logic1 files
Fix empty coverage return type and checks29 files