Skip to content

Commit 753eef9

Browse files
Merge pull request #869 from codeflash-ai/codeflash/optimize-pr868-2025-10-31T21.36.53
⚡️ Speed up method `ImportAnalyzer.visit_Attribute` by 38% in PR #868 (`import-analyser-fix`)
2 parents 668de81 + e2b83e5 commit 753eef9

File tree

1 file changed

+39
-30
lines changed

1 file changed

+39
-30
lines changed

codeflash/discovery/discover_unit_tests.py

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,18 @@ def __init__(self, function_names_to_find: set[str]) -> None:
219219
# For prefix match, store mapping from prefix-root to candidates for O(1) matching
220220
self._exact_names = function_names_to_find
221221
self._prefix_roots: dict[str, list[str]] = {}
222+
# Precompute sets for faster lookup during visit_Attribute()
223+
self._dot_names: set[str] = set()
224+
self._dot_methods: dict[str, set[str]] = {}
225+
self._class_method_to_target: dict[tuple[str, str], str] = {}
222226
for name in function_names_to_find:
223227
if "." in name:
224-
root = name.split(".", 1)[0]
225-
self._prefix_roots.setdefault(root, []).append(name)
228+
root, method = name.rsplit(".", 1)
229+
self._dot_names.add(name)
230+
self._dot_methods.setdefault(method, set()).add(root)
231+
self._class_method_to_target[(root, method)] = name
232+
root_prefix = name.split(".", 1)[0]
233+
self._prefix_roots.setdefault(root_prefix, []).append(name)
226234

227235
def visit_Import(self, node: ast.Import) -> None:
228236
"""Handle 'import module' statements."""
@@ -333,44 +341,45 @@ def visit_Attribute(self, node: ast.Attribute) -> None:
333341
if self.found_any_target_function:
334342
return
335343

344+
# Check if this is accessing a target function through an imported module
345+
346+
node_value = node.value
347+
node_attr = node.attr
348+
336349
# Check if this is accessing a target function through an imported module
337350
if (
338-
isinstance(node.value, ast.Name)
339-
and node.value.id in self.imported_modules
340-
and node.attr in self.function_names_to_find
351+
isinstance(node_value, ast.Name)
352+
and node_value.id in self.imported_modules
353+
and node_attr in self.function_names_to_find
341354
):
342355
self.found_any_target_function = True
343-
self.found_qualified_name = node.attr
356+
self.found_qualified_name = node_attr
344357
return
345358

346-
if isinstance(node.value, ast.Name) and node.value.id in self.imported_modules:
347-
for target_func in self.function_names_to_find:
348-
if "." in target_func:
349-
class_name, method_name = target_func.rsplit(".", 1)
350-
if node.attr == method_name:
351-
imported_name = node.value.id
352-
original_name = self.alias_mapping.get(imported_name, imported_name)
353-
if original_name == class_name:
354-
self.found_any_target_function = True
355-
self.found_qualified_name = target_func
356-
return
359+
# Check for methods via imported modules using precomputed _dot_methods and _class_method_to_target
360+
if isinstance(node_value, ast.Name) and node_value.id in self.imported_modules:
361+
roots_possible = self._dot_methods.get(node_attr)
362+
if roots_possible:
363+
imported_name = node_value.id
364+
original_name = self.alias_mapping.get(imported_name, imported_name)
365+
if original_name in roots_possible:
366+
self.found_any_target_function = True
367+
self.found_qualified_name = self._class_method_to_target[(original_name, node_attr)]
368+
return
357369

358370
# Check if this is accessing a method on an instance variable
359-
if isinstance(node.value, ast.Name) and node.value.id in self.instance_mapping:
360-
class_name = self.instance_mapping[node.value.id]
361-
for target_func in self.function_names_to_find:
362-
if "." in target_func:
363-
target_class, method_name = target_func.rsplit(".", 1)
364-
if node.attr == method_name and class_name == target_class:
365-
self.found_any_target_function = True
366-
self.found_qualified_name = target_func
367-
return
371+
if isinstance(node_value, ast.Name) and node_value.id in self.instance_mapping:
372+
class_name = self.instance_mapping[node_value.id]
373+
roots_possible = self._dot_methods.get(node_attr)
374+
if roots_possible and class_name in roots_possible:
375+
self.found_any_target_function = True
376+
self.found_qualified_name = self._class_method_to_target[(class_name, node_attr)]
377+
return
368378

369-
# Check if this is accessing a target function through a dynamically imported module
370-
# Only if we've detected dynamic imports are being used
371-
if self.has_dynamic_imports and node.attr in self.function_names_to_find:
379+
# Check for dynamic import match
380+
if self.has_dynamic_imports and node_attr in self.function_names_to_find:
372381
self.found_any_target_function = True
373-
self.found_qualified_name = node.attr
382+
self.found_qualified_name = node_attr
374383
return
375384

376385
self.generic_visit(node)

0 commit comments

Comments
 (0)