1010
1111import jedi
1212import libcst as cst
13+ import radon .visitors
1314from libcst .codemod import CodemodContext
1415from libcst .codemod .visitors import AddImportsVisitor , GatherImportsVisitor , RemoveImportsVisitor
1516from libcst .helpers import calculate_module_and_package
17+ from radon .complexity import cc_visit
1618
1719# from codeflash.benchmarking.pytest_new_process_trace_benchmarks import project_root
1820from codeflash .cli_cmds .console import logger
1921from codeflash .code_utils .config_consts import MAX_CONTEXT_LEN_IMPACT , TIME_LIMIT_FOR_OPT_IMPACT
20- from codeflash .models .models import CodePosition , FunctionParent
22+ from codeflash .models .models import CodePosition , FunctionParent , ImpactMetrics
2123
2224if TYPE_CHECKING :
2325 from libcst .helpers import ModuleNameAndPackage
2426
2527 from codeflash .discovery .functions_to_optimize import FunctionToOptimize
26- from codeflash .models .models import FunctionSource , ImpactMetrics
28+ from codeflash .models .models import FunctionSource
2729
2830
2931class GlobalAssignmentCollector (cst .CSTVisitor ):
@@ -1092,13 +1094,11 @@ def find_occurances(
10921094 fn_call_context += f"{ fn_definition } \n "
10931095 context_len += len (fn_definition )
10941096 fn_call_context += "```\n "
1095- opt_impact_metrics = {"calling_fn_defs" : fn_call_context }
1096- # radon metrics = get_radon_metrics(sour)
10971097 return fn_call_context
10981098
10991099
11001100def find_specific_function_in_file (
1101- source_code : str , filepath : Union [str , Path ], qualified_name : str
1101+ source_code : str , filepath : Union [str , Path ], target_function : str , target_class : str | None
11021102) -> Optional [tuple [int , int ]]:
11031103 """Find a specific function definition in a Python file and return its location.
11041104
@@ -1107,17 +1107,13 @@ def find_specific_function_in_file(
11071107 Args:
11081108 source_code: Source code string
11091109 filepath: Path to the Python file
1110- qualified_name: Qualified Name of the function to find, classname.functionname
1110+ target_function: Function Name of the function to find
1111+ target_class: Class name of the function to find
11111112
11121113 Returns:
11131114 Tuple of (line_number, column_offset) if found, None otherwise
11141115
11151116 """
1116- qualified_name_split = qualified_name .rsplit ("." , maxsplit = 1 )
1117- if len (qualified_name_split ) == 1 :
1118- target_function , target_class = qualified_name_split [0 ], None
1119- else :
1120- target_function , target_class = qualified_name_split [1 ], qualified_name_split [0 ]
11211117 script = jedi .Script (code = source_code , path = filepath )
11221118 names = script .get_names (all_scopes = True , definitions = True )
11231119 for name in names :
@@ -1134,16 +1130,16 @@ def find_specific_function_in_file(
11341130 return None # Function not found
11351131
11361132
1137- def get_fn_references_jedi (source_code : str , file_path : Path , qualified_name : str , project_root : Path ) -> list [Path ]:
1138- print (file_path , qualified_name , project_root )
1139- # Create a Jedi Script object
1140- function_position : CodePosition = find_specific_function_in_file (source_code , file_path , qualified_name )
1133+ def get_fn_references_jedi (
1134+ source_code : str , file_path : Path , project_root : Path , target_function : str , target_class : str | None
1135+ ) -> list [Path ]:
1136+ function_position : CodePosition = find_specific_function_in_file (
1137+ source_code , file_path , target_function , target_class
1138+ )
11411139 try :
11421140 script = jedi .Script (code = source_code , path = file_path , project = jedi .Project (path = project_root ))
1143-
11441141 # Get references to the function
11451142 references = script .get_references (line = function_position .line_no , column = function_position .col_no )
1146-
11471143 # Collect unique file paths where references are found
11481144 reference_files = set ()
11491145 for ref in references :
@@ -1153,9 +1149,7 @@ def get_fn_references_jedi(source_code: str, file_path: Path, qualified_name: st
11531149 # Skip the definition itself
11541150 if not (ref_path == file_path and ref .line == function_position .line_no ):
11551151 reference_files .add (ref_path )
1156-
11571152 return sorted (reference_files )
1158-
11591153 except Exception as e :
11601154 print (f"Error during Jedi analysis: { e } " )
11611155 return []
@@ -1164,9 +1158,36 @@ def get_fn_references_jedi(source_code: str, file_path: Path, qualified_name: st
11641158def get_opt_impact_metrics (
11651159 source_code : str , file_path : Path , qualified_name : str , project_root : Path , tests_root : Path
11661160) -> ImpactMetrics :
1167- # radon lib for complexity metrics
1168- # print(file_path, qualified_name, project_root, tests_root)
1169- matches = get_fn_references_jedi (
1170- source_code , file_path , qualified_name , project_root
1171- ) # jedi is not perfect, it doesn't capture aliased references
1172- return find_occurances (qualified_name , str (file_path ), matches , project_root , tests_root )
1161+ metrics = ImpactMetrics ()
1162+ try :
1163+ qualified_name_split = qualified_name .rsplit ("." , maxsplit = 1 )
1164+ if len (qualified_name_split ) == 1 :
1165+ target_function , target_class = qualified_name_split [0 ], None
1166+ else :
1167+ target_function , target_class = qualified_name_split [1 ], qualified_name_split [0 ]
1168+ matches = get_fn_references_jedi (
1169+ source_code , file_path , project_root , target_function , target_class
1170+ ) # jedi is not perfect, it doesn't capture aliased references
1171+ cyclomatic_complexity_results = cc_visit (source_code )
1172+ match_found = False
1173+ for result in cyclomatic_complexity_results :
1174+ if match_found :
1175+ break
1176+ if isinstance (result , radon .visitors .Function ) and not target_class :
1177+ if result .name == target_function :
1178+ metrics .cyclomatic_complexity = result .complexity
1179+ metrics .cyclomatic_complexity_rating = result .letter
1180+ match_found = True
1181+ elif isinstance (result , radon .visitors .Class ) and target_class : # noqa: SIM102
1182+ if result .name == target_class :
1183+ for method in result .methods :
1184+ if match_found :
1185+ break
1186+ if method .name == target_function :
1187+ metrics .cyclomatic_complexity = method .complexity
1188+ metrics .cyclomatic_complexity_rating = method .letter
1189+ match_found = True
1190+ metrics .calling_fns = find_occurances (qualified_name , str (file_path ), matches , project_root , tests_root )
1191+ except Exception as e :
1192+ logger .debug (f"Investigate { e } " )
1193+ return metrics
0 commit comments