From 47f420214ec9dc77b5e32800299bfdf877dcc2ea Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Fri, 27 Feb 2026 11:55:45 -0500 Subject: [PATCH 1/3] refactor: consolidate Python function discovery to CST path only Remove the AST-based discovery path for Python, routing all languages through the unified CST-based `_find_all_functions_via_language_support`. Delete dead code: `find_functions_with_return_statement`, `_find_all_functions_in_python_file`, `function_has_return_statement`, `function_is_a_property`, and associated constants. Fix FunctionVisitor to skip nested functions and exclude @property/@cached_property, and let parse errors propagate for correct empty-dict behavior on invalid files. --- codeflash/discovery/functions_to_optimize.py | 103 +++---------------- codeflash/languages/python/support.py | 84 +++++++-------- 2 files changed, 49 insertions(+), 138 deletions(-) diff --git a/codeflash/discovery/functions_to_optimize.py b/codeflash/discovery/functions_to_optimize.py index 56504875a..47a761f14 100644 --- a/codeflash/discovery/functions_to_optimize.py +++ b/codeflash/discovery/functions_to_optimize.py @@ -4,7 +4,6 @@ import os import random import warnings -from _ast import AsyncFunctionDef, ClassDef, FunctionDef from collections import defaultdict from functools import cache from pathlib import Path @@ -16,7 +15,7 @@ from rich.tree import Tree from codeflash.api.cfapi import get_blocklisted_functions, is_function_being_optimized_again -from codeflash.cli_cmds.console import DEBUG_MODE, console, logger +from codeflash.cli_cmds.console import console, logger from codeflash.code_utils.code_utils import ( exit_with_message, is_class_defined_in_file, @@ -47,10 +46,6 @@ from rich.text import Text -_property_id = "property" - -_ast_name = ast.Name - @dataclass(frozen=True) class FunctionProperties: @@ -91,15 +86,26 @@ def is_pytest_fixture(node: cst.FunctionDef) -> bool: return True return False + @staticmethod + def is_property(node: cst.FunctionDef) -> bool: + for decorator in node.decorators: + dec = decorator.decorator + if isinstance(dec, cst.Name) and dec.value in ("property", "cached_property"): + return True + return False + def visit_FunctionDef(self, node: cst.FunctionDef) -> None: return_visitor: ReturnStatementVisitor = ReturnStatementVisitor() node.visit(return_visitor) - if return_visitor.has_return_statement and not self.is_pytest_fixture(node): + if return_visitor.has_return_statement and not self.is_pytest_fixture(node) and not self.is_property(node): pos: CodeRange = self.get_metadata(cst.metadata.PositionProvider, node) parents: CSTNode | None = self.get_metadata(cst.metadata.ParentNodeProvider, node) ast_parents: list[FunctionParent] = [] while parents is not None: - if isinstance(parents, (cst.FunctionDef, cst.ClassDef)): + if isinstance(parents, cst.FunctionDef): + # Skip nested functions — only discover top-level and class-level functions + return + if isinstance(parents, cst.ClassDef): ast_parents.append(FunctionParent(parents.name.value, parents.__class__.__name__)) parents = self.get_metadata(cst.metadata.ParentNodeProvider, parents, default=None) self.functions.append( @@ -114,32 +120,6 @@ def visit_FunctionDef(self, node: cst.FunctionDef) -> None: ) -def find_functions_with_return_statement(ast_module: ast.Module, file_path: Path) -> list[FunctionToOptimize]: - results: list[FunctionToOptimize] = [] - # (node, parent_path) — iterative DFS avoids RecursionError on deeply nested ASTs - stack: list[tuple[ast.AST, list[FunctionParent]]] = [(ast_module, [])] - while stack: - node, ast_path = stack.pop() - if isinstance(node, (FunctionDef, AsyncFunctionDef)): - if function_has_return_statement(node) and not function_is_a_property(node): - results.append( - FunctionToOptimize( - function_name=node.name, - file_path=file_path, - parents=ast_path[:], - is_async=isinstance(node, AsyncFunctionDef), - ) - ) - # Don't recurse into function bodies (matches original visitor behaviour) - continue - child_path = ( - [*ast_path, FunctionParent(node.name, node.__class__.__name__)] if isinstance(node, ClassDef) else ast_path - ) - for child in reversed(list(ast.iter_child_nodes(node))): - stack.append((child, child_path)) - return results - - # ============================================================================= # Multi-language support helpers # ============================================================================= @@ -250,23 +230,6 @@ def _is_js_ts_function_exported(file_path: Path, function_name: str) -> tuple[bo return True, None -def _find_all_functions_in_python_file(file_path: Path) -> dict[Path, list[FunctionToOptimize]]: - """Find all optimizable functions in a Python file using AST parsing. - - This is the original Python implementation preserved for backward compatibility. - """ - functions: dict[Path, list[FunctionToOptimize]] = {} - with file_path.open(encoding="utf8") as f: - try: - ast_module = ast.parse(f.read()) - except Exception as e: - if DEBUG_MODE: - logger.exception(e) - return functions - functions[file_path] = find_functions_with_return_statement(ast_module, file_path) - return functions - - def _find_all_functions_via_language_support(file_path: Path) -> dict[Path, list[FunctionToOptimize]]: """Find all optimizable functions using the language support abstraction. @@ -545,16 +508,6 @@ def find_all_functions_in_file(file_path: Path) -> dict[Path, list[FunctionToOpt if not is_language_supported(file_path): return {} - try: - lang_support = get_language_support(file_path) - except Exception: - return {} - - # Route to Python-specific implementation for backward compatibility - if lang_support.language == Language.PYTHON: - return _find_all_functions_in_python_file(file_path) - - # Use language support abstraction for other languages return _find_all_functions_via_language_support(file_path) @@ -984,31 +937,3 @@ def filter_files_optimized(file_path: Path, tests_root: Path, ignore_paths: list file_path in submodule_paths or any(file_path.is_relative_to(submodule_path) for submodule_path in submodule_paths) ) - - -def function_has_return_statement(function_node: FunctionDef | AsyncFunctionDef) -> bool: - # Custom DFS, return True as soon as a Return node is found - stack: list[ast.AST] = list(function_node.body) - while stack: - node = stack.pop() - if isinstance(node, ast.Return): - return True - # Only push child nodes that are statements; Return nodes are statements, - # so this preserves correctness while avoiding unnecessary traversal into expr/Name/etc. - for field in getattr(node, "_fields", ()): - child = getattr(node, field, None) - if isinstance(child, list): - for item in child: - if isinstance(item, ast.stmt): - stack.append(item) - elif isinstance(child, ast.stmt): - stack.append(child) - return False - - -def function_is_a_property(function_node: FunctionDef | AsyncFunctionDef) -> bool: - for node in function_node.decorator_list: # noqa: SIM110 - # Use isinstance rather than type(...) is ... for better performance with single inheritance trees like ast - if isinstance(node, _ast_name) and node.id == _property_id: - return True - return False diff --git a/codeflash/languages/python/support.py b/codeflash/languages/python/support.py index cf55e6f61..ae0c8ae0b 100644 --- a/codeflash/languages/python/support.py +++ b/codeflash/languages/python/support.py @@ -130,56 +130,42 @@ def discover_functions( criteria = filter_criteria or FunctionFilterCriteria() - try: - # Read and parse the file using libcst with metadata - source = file_path.read_text(encoding="utf-8") - try: - tree = cst.parse_module(source) - except Exception: - return [] - - # Use the libcst-based FunctionVisitor for accurate line numbers - wrapper = cst.metadata.MetadataWrapper(tree) - function_visitor = FunctionVisitor(file_path=str(file_path)) - wrapper.visit(function_visitor) - - functions: list[FunctionToOptimize] = [] - for func in function_visitor.functions: - if not isinstance(func, FunctionToOptimize): - continue - - # Apply filter criteria - if not criteria.include_async and func.is_async: - continue - - if not criteria.include_methods and func.parents: - continue - - # Check for return statement requirement (FunctionVisitor already filters this) - # but we double-check here for consistency - if criteria.require_return and func.starting_line is None: - continue - - # Add is_method field based on parents - func_with_is_method = FunctionToOptimize( - function_name=func.function_name, - file_path=file_path, - parents=func.parents, - starting_line=func.starting_line, - ending_line=func.ending_line, - starting_col=func.starting_col, - ending_col=func.ending_col, - is_async=func.is_async, - is_method=len(func.parents) > 0 and any(p.type == "ClassDef" for p in func.parents), - language="python", - ) - functions.append(func_with_is_method) - - return functions + source = file_path.read_text(encoding="utf-8") + tree = cst.parse_module(source) + + wrapper = cst.metadata.MetadataWrapper(tree) + function_visitor = FunctionVisitor(file_path=str(file_path)) + wrapper.visit(function_visitor) + + functions: list[FunctionToOptimize] = [] + for func in function_visitor.functions: + if not isinstance(func, FunctionToOptimize): + continue + + if not criteria.include_async and func.is_async: + continue + + if not criteria.include_methods and func.parents: + continue + + if criteria.require_return and func.starting_line is None: + continue + + func_with_is_method = FunctionToOptimize( + function_name=func.function_name, + file_path=file_path, + parents=func.parents, + starting_line=func.starting_line, + ending_line=func.ending_line, + starting_col=func.starting_col, + ending_col=func.ending_col, + is_async=func.is_async, + is_method=len(func.parents) > 0 and any(p.type == "ClassDef" for p in func.parents), + language="python", + ) + functions.append(func_with_is_method) - except Exception as e: - logger.warning("Failed to discover functions in %s: %s", file_path, e) - return [] + return functions def discover_tests( self, test_root: Path, source_functions: Sequence[FunctionToOptimize] From 91cf6ea2aa3bb45d62b609822bd451da01853591 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Fri, 27 Feb 2026 12:08:42 -0500 Subject: [PATCH 2/3] fix: update tests for consolidated CST discovery behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nested functions are now skipped by FunctionVisitor, and discover_functions no longer swallows parse/IO errors — callers handle them. Update test expectations accordingly. --- codeflash/discovery/functions_to_optimize.py | 1 - tests/test_languages/test_language_parity.py | 29 ++++------- tests/test_languages/test_python_support.py | 52 +++++++------------- 3 files changed, 27 insertions(+), 55 deletions(-) diff --git a/codeflash/discovery/functions_to_optimize.py b/codeflash/discovery/functions_to_optimize.py index 47a761f14..80f7806c0 100644 --- a/codeflash/discovery/functions_to_optimize.py +++ b/codeflash/discovery/functions_to_optimize.py @@ -243,7 +243,6 @@ def _find_all_functions_via_language_support(file_path: Path) -> dict[Path, list try: lang_support = get_language_support(file_path) criteria = FunctionFilterCriteria(require_return=True) - # discover_functions already returns FunctionToOptimize objects functions[file_path] = lang_support.discover_functions(file_path, criteria) except Exception as e: logger.debug(f"Failed to discover functions in {file_path}: {e}") diff --git a/tests/test_languages/test_language_parity.py b/tests/test_languages/test_language_parity.py index 2b2035c84..ab4a63e39 100644 --- a/tests/test_languages/test_language_parity.py +++ b/tests/test_languages/test_language_parity.py @@ -440,32 +440,23 @@ def test_async_functions_discovery(self, python_support, js_support): assert js_sync.is_async is False, "JavaScript sync function should have is_async=False" def test_nested_functions_discovery(self, python_support, js_support): - """Both should discover nested functions with parent info.""" + """Python skips nested functions; JavaScript discovers them with parent info.""" py_file = write_temp_file(NESTED_FUNCTIONS.python, ".py") js_file = write_temp_file(NESTED_FUNCTIONS.javascript, ".js") py_funcs = python_support.discover_functions(py_file) js_funcs = js_support.discover_functions(js_file) - # Both should find 2 functions (outer and inner) - assert len(py_funcs) == 2, f"Python found {len(py_funcs)}, expected 2" - assert len(js_funcs) == 2, f"JavaScript found {len(js_funcs)}, expected 2" + # Python skips nested functions — only outer is discovered + assert len(py_funcs) == 1, f"Python found {len(py_funcs)}, expected 1" + assert py_funcs[0].function_name == "outer" - # Check names - py_names = {f.function_name for f in py_funcs} + # JavaScript discovers both + assert len(js_funcs) == 2, f"JavaScript found {len(js_funcs)}, expected 2" js_names = {f.function_name for f in js_funcs} - - assert py_names == {"outer", "inner"}, f"Python found {py_names}" assert js_names == {"outer", "inner"}, f"JavaScript found {js_names}" - # Check parent info for inner function - py_inner = next(f for f in py_funcs if f.function_name == "inner") js_inner = next(f for f in js_funcs if f.function_name == "inner") - - assert len(py_inner.parents) >= 1, "Python inner should have parent info" - assert py_inner.parents[0].name == "outer", "Python inner's parent should be outer" - - # JavaScript nested function parent check assert len(js_inner.parents) >= 1, "JavaScript inner should have parent info" assert js_inner.parents[0].name == "outer", "JavaScript inner's parent should be outer" @@ -554,11 +545,11 @@ def test_filter_exclude_methods(self, python_support, js_support): assert js_funcs[0].function_name == "standalone" def test_nonexistent_file_returns_empty(self, python_support, js_support): - """Both should return empty list for nonexistent files.""" - py_funcs = python_support.discover_functions(Path("/nonexistent/file.py")) - js_funcs = js_support.discover_functions(Path("/nonexistent/file.js")) + """Python raises on nonexistent files; JavaScript returns empty list.""" + with pytest.raises(FileNotFoundError): + python_support.discover_functions(Path("/nonexistent/file.py")) - assert py_funcs == [] + js_funcs = js_support.discover_functions(Path("/nonexistent/file.js")) assert js_funcs == [] def test_line_numbers_captured(self, python_support, js_support): diff --git a/tests/test_languages/test_python_support.py b/tests/test_languages/test_python_support.py index e4755cf8e..8d5321d81 100644 --- a/tests/test_languages/test_python_support.py +++ b/tests/test_languages/test_python_support.py @@ -137,7 +137,7 @@ def sync_function(): assert sync_func.is_async is False def test_discover_nested_functions(self, python_support): - """Test discovering nested functions.""" + """Test that nested functions are excluded — only top-level and class-level functions are discovered.""" with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f: f.write(""" def outer(): @@ -149,16 +149,9 @@ def inner(): functions = python_support.discover_functions(Path(f.name)) - # Both outer and inner should be discovered - assert len(functions) == 2 - names = {func.function_name for func in functions} - assert names == {"outer", "inner"} - - # Inner should have outer as parent - inner = next(f for f in functions if f.function_name == "inner") - assert len(inner.parents) == 1 - assert inner.parents[0].name == "outer" - assert inner.parents[0].type == "FunctionDef" + # Only outer should be discovered; inner is nested and skipped + assert len(functions) == 1 + assert functions[0].function_name == "outer" def test_discover_static_method(self, python_support): """Test discovering static methods.""" @@ -237,19 +230,21 @@ def func2(): assert func2.starting_line == 4 assert func2.ending_line == 7 - def test_discover_invalid_file_returns_empty(self, python_support): - """Test that invalid Python file returns empty list.""" + def test_discover_invalid_file_raises(self, python_support): + """Test that invalid Python file raises a parse error.""" + from libcst._exceptions import ParserSyntaxError + with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f: f.write("this is not valid python {{{{") f.flush() - functions = python_support.discover_functions(Path(f.name)) - assert functions == [] + with pytest.raises(ParserSyntaxError): + python_support.discover_functions(Path(f.name)) - def test_discover_nonexistent_file_returns_empty(self, python_support): - """Test that nonexistent file returns empty list.""" - functions = python_support.discover_functions(Path("/nonexistent/file.py")) - assert functions == [] + def test_discover_nonexistent_file_raises(self, python_support): + """Test that nonexistent file raises FileNotFoundError.""" + with pytest.raises(FileNotFoundError): + python_support.discover_functions(Path("/nonexistent/file.py")) class TestReplaceFunction: @@ -584,12 +579,7 @@ def process(value): return helper_function(value) + 1 """) - func = FunctionToOptimize( - function_name="helper_function", - file_path=source_file, - starting_line=1, - ending_line=2, - ) + func = FunctionToOptimize(function_name="helper_function", file_path=source_file, starting_line=1, ending_line=2) refs = python_support.find_references(func, project_root=tmp_path) @@ -646,12 +636,7 @@ def test_find_references_no_references(python_support, tmp_path): return 42 """) - func = FunctionToOptimize( - function_name="isolated_function", - file_path=source_file, - starting_line=1, - ending_line=2, - ) + func = FunctionToOptimize(function_name="isolated_function", file_path=source_file, starting_line=1, ending_line=2) refs = python_support.find_references(func, project_root=tmp_path) @@ -668,10 +653,7 @@ def test_find_references_nonexistent_function(python_support, tmp_path): """) func = FunctionToOptimize( - function_name="nonexistent_function", - file_path=source_file, - starting_line=1, - ending_line=2, + function_name="nonexistent_function", file_path=source_file, starting_line=1, ending_line=2 ) refs = python_support.find_references(func, project_root=tmp_path) From 2b2caf4995143c936f16681db910a448a05cbe46 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Fri, 27 Feb 2026 12:38:50 -0500 Subject: [PATCH 3/3] fix: resolve mypy errors in discovery and support files - Change FunctionVisitor.file_path from str to Path - Unify dict keys to Path across discovery functions (get_all_files_and_functions, get_functions_within_lines, get_functions_within_git_diff, etc.) - Remove redundant isinstance check in discover_functions - Add assert for found_function narrowing after exit_with_message - Fix closest_matching_file_function_name return type narrowing --- codeflash/discovery/functions_to_optimize.py | 34 ++++++++++---------- codeflash/languages/python/support.py | 5 +-- codeflash/lsp/beta.py | 2 +- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/codeflash/discovery/functions_to_optimize.py b/codeflash/discovery/functions_to_optimize.py index 80f7806c0..62d6896fe 100644 --- a/codeflash/discovery/functions_to_optimize.py +++ b/codeflash/discovery/functions_to_optimize.py @@ -68,9 +68,9 @@ def visit_Return(self, node: cst.Return) -> None: class FunctionVisitor(cst.CSTVisitor): METADATA_DEPENDENCIES = (cst.metadata.PositionProvider, cst.metadata.ParentNodeProvider) - def __init__(self, file_path: str) -> None: + def __init__(self, file_path: Path) -> None: super().__init__() - self.file_path: str = file_path + self.file_path: Path = file_path self.functions: list[FunctionToOptimize] = [] @staticmethod @@ -264,7 +264,7 @@ def get_functions_to_optimize( assert sum([bool(optimize_all), bool(replay_test), bool(file)]) <= 1, ( "Only one of optimize_all, replay_test, or file should be provided" ) - functions: dict[str, list[FunctionToOptimize]] + functions: dict[Path, list[FunctionToOptimize]] trace_file_path: Path | None = None is_lsp = is_LSP_enabled() with warnings.catch_warnings(): @@ -281,7 +281,7 @@ def get_functions_to_optimize( logger.info("!lsp|Finding all functions in the file '%s'…", file) console.rule() file = Path(file) if isinstance(file, str) else file - functions: dict[Path, list[FunctionToOptimize]] = find_all_functions_in_file(file) + functions = find_all_functions_in_file(file) if only_get_this_function is not None: split_function = only_get_this_function.split(".") if len(split_function) > 2: @@ -316,6 +316,7 @@ def get_functions_to_optimize( f"Function {only_get_this_function} not found in file {file}\nor the function does not have a 'return' statement or is a property" ) + assert found_function is not None # For JavaScript/TypeScript, verify that the function (or its parent class) is exported # Non-exported functions cannot be imported by tests if found_function.language in ("javascript", "typescript"): @@ -359,7 +360,7 @@ def get_functions_to_optimize( return filtered_modified_functions, functions_count, trace_file_path -def get_functions_within_git_diff(uncommitted_changes: bool) -> dict[str, list[FunctionToOptimize]]: +def get_functions_within_git_diff(uncommitted_changes: bool) -> dict[Path, list[FunctionToOptimize]]: modified_lines: dict[str, list[int]] = get_git_diff(uncommitted_changes=uncommitted_changes) return get_functions_within_lines(modified_lines) @@ -400,7 +401,7 @@ def closest_matching_file_function_name( closest_match = function closest_file = file_path - if closest_match is not None: + if closest_match is not None and closest_file is not None: return closest_file, closest_match return None @@ -434,13 +435,13 @@ def levenshtein_distance(s1: str, s2: str) -> int: return previous[len1] -def get_functions_inside_a_commit(commit_hash: str) -> dict[str, list[FunctionToOptimize]]: +def get_functions_inside_a_commit(commit_hash: str) -> dict[Path, list[FunctionToOptimize]]: modified_lines: dict[str, list[int]] = get_git_diff(only_this_commit=commit_hash) return get_functions_within_lines(modified_lines) -def get_functions_within_lines(modified_lines: dict[str, list[int]]) -> dict[str, list[FunctionToOptimize]]: - functions: dict[str, list[FunctionToOptimize]] = {} +def get_functions_within_lines(modified_lines: dict[str, list[int]]) -> dict[Path, list[FunctionToOptimize]]: + functions: dict[Path, list[FunctionToOptimize]] = {} for path_str, lines_in_file in modified_lines.items(): path = Path(path_str) if not path.exists(): @@ -452,9 +453,9 @@ def get_functions_within_lines(modified_lines: dict[str, list[int]]) -> dict[str except Exception as e: logger.exception(e) continue - function_lines = FunctionVisitor(file_path=str(path)) + function_lines = FunctionVisitor(file_path=path) wrapper.visit(function_lines) - functions[str(path)] = [ + functions[path] = [ function_to_optimize for function_to_optimize in function_lines.functions if (start_line := function_to_optimize.starting_line) is not None @@ -466,7 +467,7 @@ def get_functions_within_lines(modified_lines: dict[str, list[int]]) -> dict[str def get_all_files_and_functions( module_root_path: Path, ignore_paths: list[Path], language: Language | None = None -) -> dict[str, list[FunctionToOptimize]]: +) -> dict[Path, list[FunctionToOptimize]]: """Get all optimizable functions from files in the module root. Args: @@ -478,9 +479,8 @@ def get_all_files_and_functions( Dictionary mapping file paths to lists of FunctionToOptimize. """ - functions: dict[str, list[FunctionToOptimize]] = {} + functions: dict[Path, list[FunctionToOptimize]] = {} for file_path in get_files_for_language(module_root_path, ignore_paths, language): - # Find all the functions in the file functions.update(find_all_functions_in_file(file_path).items()) # Randomize the order of the files to optimize to avoid optimizing the same file in the same order every time. # Helpful if an optimize-all run is stuck and we restart it. @@ -785,7 +785,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]] = {} + filtered_modified_functions: dict[Path, list[FunctionToOptimize]] = {} blocklist_funcs = get_blocklisted_functions() logger.debug(f"Blocklisted functions: {blocklist_funcs}") # Remove any function that we don't want to optimize @@ -892,7 +892,7 @@ def is_test_file(file_path_normalized: str) -> bool: functions_tmp.append(function) _functions = functions_tmp - filtered_modified_functions[file_path] = _functions + filtered_modified_functions[file_path_path] = _functions functions_count += len(_functions) if not disable_logs: @@ -913,7 +913,7 @@ def is_test_file(file_path_normalized: str) -> bool: if len(tree.children) > 0: console.print(tree) console.rule() - return {Path(k): v for k, v in filtered_modified_functions.items() if v}, functions_count + return {k: v for k, v in filtered_modified_functions.items() if v}, functions_count def filter_files_optimized(file_path: Path, tests_root: Path, ignore_paths: list[Path], module_root: Path) -> bool: diff --git a/codeflash/languages/python/support.py b/codeflash/languages/python/support.py index ae0c8ae0b..8567aec01 100644 --- a/codeflash/languages/python/support.py +++ b/codeflash/languages/python/support.py @@ -134,14 +134,11 @@ def discover_functions( tree = cst.parse_module(source) wrapper = cst.metadata.MetadataWrapper(tree) - function_visitor = FunctionVisitor(file_path=str(file_path)) + function_visitor = FunctionVisitor(file_path=file_path) wrapper.visit(function_visitor) functions: list[FunctionToOptimize] = [] for func in function_visitor.functions: - if not isinstance(func, FunctionToOptimize): - continue - if not criteria.include_async and func.is_async: continue diff --git a/codeflash/lsp/beta.py b/codeflash/lsp/beta.py index 75f761f19..a1c09770b 100644 --- a/codeflash/lsp/beta.py +++ b/codeflash/lsp/beta.py @@ -114,7 +114,7 @@ def get_functions_in_commit(params: OptimizableFunctionsInCommitParams) -> dict[ return {"functions": file_to_qualified_names, "status": "success"} -def _group_functions_by_file(functions: dict[str, list[FunctionToOptimize]]) -> dict[str, list[str]]: +def _group_functions_by_file(functions: dict[Path, list[FunctionToOptimize]]) -> dict[str, list[str]]: file_to_funcs_to_optimize, _ = filter_functions( modified_functions=functions, tests_root=server.optimizer.test_cfg.tests_root,