-
Notifications
You must be signed in to change notification settings - Fork 18
follow up on pre-filtering PR & better exit message UX #310
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
Changes from all commits
a73d2d4
fee08a6
36f320d
0fcd59c
a928fc0
dae4afc
455c507
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -141,144 +141,175 @@ def close(self) -> None: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class ImportAnalyzer(ast.NodeVisitor): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""AST-based analyzer to find all imports in a test file.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""AST-based analyzer to check if any qualified names from function_names_to_find are imported or used in a test file.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def __init__(self, function_names_to_find: set[str]) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.function_names_to_find = function_names_to_find | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_names: set[str] = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_any_target_function: bool = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_qualified_name = None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_modules: set[str] = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_target_functions: set[str] = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.qualified_names_called: set[str] = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.has_dynamic_imports: bool = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.wildcard_modules: set[str] = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def visit_Import(self, node: ast.Import) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Handle 'import module' statements.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if self.found_any_target_function: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for alias in node.names: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
module_name = alias.asname if alias.asname else alias.name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_modules.add(module_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_names.add(module_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.generic_visit(node) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check for dynamic import modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if alias.name == "importlib": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.has_dynamic_imports = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check if module itself is a target qualified name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if module_name in self.function_names_to_find: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_any_target_function = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_qualified_name = module_name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check if any target qualified name starts with this module | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for target_func in self.function_names_to_find: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if target_func.startswith(f"{module_name}."): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_any_target_function = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_qualified_name = target_func | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def visit_ImportFrom(self, node: ast.ImportFrom) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Handle 'from module import name' statements.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if node.module: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_modules.add(node.module) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if self.found_any_target_function: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if not node.module: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for alias in node.names: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if alias.name == "*": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imported_name = alias.asname if alias.asname else alias.name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_names.add(imported_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if alias.name in self.function_names_to_find: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_target_functions.add(alias.name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check for qualified name matches | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if node.module: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.wildcard_modules.add(node.module) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imported_name = alias.asname if alias.asname else alias.name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.imported_modules.add(imported_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check for dynamic import functions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if node.module == "importlib" and alias.name == "import_module": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.has_dynamic_imports = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check if imported name is a target qualified name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if alias.name in self.function_names_to_find: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_any_target_function = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_qualified_name = alias.name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check if module.name forms a target qualified name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
qualified_name = f"{node.module}.{alias.name}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if qualified_name in self.function_names_to_find: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_target_functions.add(qualified_name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.generic_visit(node) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_any_target_function = True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.found_qualified_name = qualified_name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+181
to
+209
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚡️Codeflash found 31% (0.31x) speedup for
|
Test | Status |
---|---|
⚙️ Existing Unit Tests | 🔘 None Found |
🌀 Generated Regression Tests | ✅ 1106 Passed |
⏪ Replay Tests | 🔘 None Found |
🔎 Concolic Coverage Tests | 🔘 None Found |
📊 Tests Coverage | 100.0% |
🌀 Generated Regression Tests Details
from __future__ import annotations
import ast
# imports
import pytest # used for our unit tests
from codeflash.discovery.discover_unit_tests import ImportAnalyzer
# unit tests
def make_importfrom_node(module, names):
"""
Helper to create an ast.ImportFrom node.
names: list of (name, asname) tuples.
"""
return ast.ImportFrom(
module=module,
names=[ast.alias(name=n, asname=a) for n, a in names],
level=0,
lineno=1,
col_offset=0
)
# ---------------------------
# 1. Basic Test Cases
# ---------------------------
def test_importfrom_basic_name_found():
# from math import sqrt
node = make_importfrom_node("math", [("sqrt", None)])
analyzer = ImportAnalyzer({"sqrt"})
analyzer.visit_ImportFrom(node)
def test_importfrom_basic_qualified_name_found():
# from math import sqrt
node = make_importfrom_node("math", [("sqrt", None)])
analyzer = ImportAnalyzer({"math.sqrt"})
analyzer.visit_ImportFrom(node)
def test_importfrom_basic_asname_import():
# from os.path import join as path_join
node = make_importfrom_node("os.path", [("join", "path_join")])
analyzer = ImportAnalyzer({"os.path.join"})
analyzer.visit_ImportFrom(node)
def test_importfrom_basic_no_target_found():
# from collections import deque
node = make_importfrom_node("collections", [("deque", None)])
analyzer = ImportAnalyzer({"Counter"})
analyzer.visit_ImportFrom(node)
def test_importfrom_multiple_names_one_target():
# from math import sqrt, cos
node = make_importfrom_node("math", [("sqrt", None), ("cos", None)])
analyzer = ImportAnalyzer({"cos"})
analyzer.visit_ImportFrom(node)
def test_importfrom_multiple_names_no_target():
# from math import sqrt, cos
node = make_importfrom_node("math", [("sqrt", None), ("cos", None)])
analyzer = ImportAnalyzer({"tan"})
analyzer.visit_ImportFrom(node)
def test_importfrom_importlib_import_module_dynamic():
# from importlib import import_module
node = make_importfrom_node("importlib", [("import_module", None)])
analyzer = ImportAnalyzer({"import_module"})
analyzer.visit_ImportFrom(node)
def test_importfrom_importlib_import_module_not_target():
# from importlib import import_module
node = make_importfrom_node("importlib", [("import_module", None)])
analyzer = ImportAnalyzer({"something_else"})
analyzer.visit_ImportFrom(node)
def test_importfrom_wildcard_import():
# from math import *
node = make_importfrom_node("math", [("*", None)])
analyzer = ImportAnalyzer({"sqrt"})
analyzer.visit_ImportFrom(node)
# ---------------------------
# 2. Edge Test Cases
# ---------------------------
def test_importfrom_empty_module():
# from . import foo (module=None)
node = ast.ImportFrom(module=None, names=[ast.alias(name="foo", asname=None)], level=1)
analyzer = ImportAnalyzer({"foo"})
analyzer.visit_ImportFrom(node)
def test_importfrom_empty_names():
# from math import (no names)
node = make_importfrom_node("math", [])
analyzer = ImportAnalyzer({"sqrt"})
analyzer.visit_ImportFrom(node)
def test_importfrom_duplicate_names():
# from math import sqrt, sqrt
node = make_importfrom_node("math", [("sqrt", None), ("sqrt", None)])
analyzer = ImportAnalyzer({"sqrt"})
analyzer.visit_ImportFrom(node)
def test_importfrom_asname_and_name_both_targets():
# from foo import bar as baz, bar
node = make_importfrom_node("foo", [("bar", "baz"), ("bar", None)])
analyzer = ImportAnalyzer({"foo.bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_found_any_target_short_circuit():
# from math import sqrt, cos, tan
node = make_importfrom_node("math", [("sqrt", None), ("cos", None), ("tan", None)])
analyzer = ImportAnalyzer({"cos"})
# Set found_any_target_function to True before visiting
analyzer.found_any_target_function = True
analyzer.visit_ImportFrom(node)
def test_importfrom_star_and_normal_imports():
# from foo import *, bar
node = make_importfrom_node("foo", [("*", None), ("bar", None)])
analyzer = ImportAnalyzer({"foo.bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_asname_is_target():
# from foo import bar as baz
node = make_importfrom_node("foo", [("bar", "baz")])
analyzer = ImportAnalyzer({"baz"})
analyzer.visit_ImportFrom(node)
def test_importfrom_module_with_dot():
# from foo.bar import baz
node = make_importfrom_node("foo.bar", [("baz", None)])
analyzer = ImportAnalyzer({"foo.bar.baz"})
analyzer.visit_ImportFrom(node)
def test_importfrom_module_is_empty_string():
# from "" import foo (invalid, but test for robustness)
node = make_importfrom_node("", [("foo", None)])
analyzer = ImportAnalyzer({"foo"})
analyzer.visit_ImportFrom(node)
# ---------------------------
# 3. Large Scale Test Cases
# ---------------------------
def test_importfrom_many_names_one_target():
# from mod import a0, a1, ..., a999 (target is a789)
names = [(f"a{i}", None) for i in range(1000)]
node = make_importfrom_node("mod", names)
analyzer = ImportAnalyzer({"a789"})
analyzer.visit_ImportFrom(node)
def test_importfrom_many_names_no_target():
# from mod import a0, a1, ..., a999 (no target)
names = [(f"a{i}", None) for i in range(1000)]
node = make_importfrom_node("mod", names)
analyzer = ImportAnalyzer({"not_present"})
analyzer.visit_ImportFrom(node)
def test_importfrom_many_wildcard_imports():
# from mod0 import *, from mod1 import *, ..., from mod9 import *
analyzer = ImportAnalyzer({"something"})
for i in range(10):
node = make_importfrom_node(f"mod{i}", [("*", None)])
analyzer.visit_ImportFrom(node)
def test_importfrom_many_importlib_import_module():
# from importlib import import_module as imp0, imp1, ..., imp999
names = [("import_module", f"imp{i}") for i in range(1000)]
node = make_importfrom_node("importlib", names)
analyzer = ImportAnalyzer({"not_present"})
analyzer.visit_ImportFrom(node)
# All asnames should be in imported_modules
for i in range(1000):
pass
def test_importfrom_many_targets_short_circuit():
# from mod import a0, ..., a999 (targets: a0, a500, a999)
names = [(f"a{i}", None) for i in range(1000)]
node = make_importfrom_node("mod", names)
analyzer = ImportAnalyzer({"a0", "a500", "a999"})
analyzer.visit_ImportFrom(node)
def test_importfrom_large_qualified_names():
# from mod import a0, ..., a999 (targets: mod.a0, mod.a999)
names = [(f"a{i}", None) for i in range(1000)]
node = make_importfrom_node("mod", names)
analyzer = ImportAnalyzer({"mod.a0", "mod.a999"})
analyzer.visit_ImportFrom(node)
def test_importfrom_large_asname_targets():
# from mod import a0 as b0, ..., a999 as b999 (target: mod.a999)
names = [(f"a{i}", f"b{i}") for i in range(1000)]
node = make_importfrom_node("mod", names)
analyzer = ImportAnalyzer({"mod.a999"})
analyzer.visit_ImportFrom(node)
def test_importfrom_large_asname_non_target():
# from mod import a0 as b0, ..., a999 as b999 (target: b123)
names = [(f"a{i}", f"b{i}") for i in range(1000)]
node = make_importfrom_node("mod", names)
analyzer = ImportAnalyzer({"b123"})
analyzer.visit_ImportFrom(node)
# 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.discovery.discover_unit_tests import ImportAnalyzer
# unit tests
def parse_first_importfrom(src: str) -> ast.ImportFrom:
"""Utility to parse the first ImportFrom node from a code snippet."""
for node in ast.walk(ast.parse(src)):
if isinstance(node, ast.ImportFrom):
return node
raise ValueError("No ImportFrom found in source.")
# ---------------------------
# 1. Basic Test Cases
# ---------------------------
def test_simple_import_detects_target_name():
# from foo import bar
src = "from foo import bar"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_simple_import_detects_qualified_target():
# from foo import bar
src = "from foo import bar"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"foo.bar"})
analyzer.visit_ImportFrom(node)
def test_import_with_alias_detects_target():
# from foo import bar as baz
src = "from foo import bar as baz"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_import_with_alias_detects_qualified_target():
# from foo import bar as baz
src = "from foo import bar as baz"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"foo.bar"})
analyzer.visit_ImportFrom(node)
def test_import_multiple_names_detects_any_target():
# from foo import bar, baz
src = "from foo import bar, baz"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"baz"})
analyzer.visit_ImportFrom(node)
def test_import_multiple_names_detects_qualified_target():
# from foo import bar, baz
src = "from foo import bar, baz"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"foo.baz"})
analyzer.visit_ImportFrom(node)
def test_import_no_target_found():
# from foo import bar
src = "from foo import bar"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"qux"})
analyzer.visit_ImportFrom(node)
def test_importlib_import_module_sets_dynamic():
# from importlib import import_module
src = "from importlib import import_module"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"something"})
analyzer.visit_ImportFrom(node)
def test_importlib_import_module_with_alias_sets_dynamic():
# from importlib import import_module as im
src = "from importlib import import_module as im"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"something"})
analyzer.visit_ImportFrom(node)
def test_wildcard_import_records_module():
# from foo import *
src = "from foo import *"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
# ---------------------------
# 2. Edge Test Cases
# ---------------------------
def test_importfrom_with_no_module():
# from . import bar
src = "from . import bar"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_empty_names():
# from foo import
# This is a syntax error, so let's simulate an ImportFrom node with empty names
node = ast.ImportFrom(module="foo", names=[], level=0)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_duplicate_names():
# from foo import bar, bar
src = "from foo import bar, bar"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_star_and_names():
# from foo import *, bar
# Not valid Python, but let's simulate it
node = ast.ImportFrom(module="foo", names=[ast.alias(name="*", asname=None), ast.alias(name="bar", asname=None)], level=0)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_level_relative_import():
# from ..foo import bar
node = ast.ImportFrom(module="foo", names=[ast.alias(name="bar", asname=None)], level=2)
analyzer = ImportAnalyzer({"bar"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_asname_target():
# from foo import bar as baz
src = "from foo import bar as baz"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"baz"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_module_dot_in_target():
# from foo.bar import baz
src = "from foo.bar import baz"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"foo.bar.baz"})
analyzer.visit_ImportFrom(node)
def test_importfrom_with_nonascii_names():
# from модуль import функция
node = ast.ImportFrom(module="модуль", names=[ast.alias(name="функция", asname=None)], level=0)
analyzer = ImportAnalyzer({"функция", "модуль.функция"})
analyzer.visit_ImportFrom(node)
# ---------------------------
# 3. Large Scale Test Cases
# ---------------------------
def test_importfrom_large_number_of_names():
# from foo import name0, name1, ..., name999
names = [f"name{i}" for i in range(1000)]
src = "from foo import " + ", ".join(names)
node = parse_first_importfrom(src)
target = f"name500"
analyzer = ImportAnalyzer({target})
analyzer.visit_ImportFrom(node)
def test_importfrom_large_number_of_targets():
# from foo import bar
src = "from foo import bar"
node = parse_first_importfrom(src)
# 1000 possible targets, only one matches
targets = {f"bar{i}" for i in range(999)}
targets.add("bar")
analyzer = ImportAnalyzer(targets)
analyzer.visit_ImportFrom(node)
def test_importfrom_large_number_of_wildcard_imports():
# Simulate 1000 wildcard imports from different modules
analyzer = ImportAnalyzer({"something"})
for i in range(1000):
node = ast.ImportFrom(module=f"mod{i}", names=[ast.alias(name="*", asname=None)], level=0)
analyzer.visit_ImportFrom(node)
def test_importfrom_performance_with_many_targets_and_names():
# from foo import name0, name1, ..., name999
names = [f"name{i}" for i in range(1000)]
src = "from foo import " + ", ".join(names)
node = parse_first_importfrom(src)
# 1000 targets, only one matches
targets = {f"name{i}" for i in range(1000)}
analyzer = ImportAnalyzer(targets)
analyzer.visit_ImportFrom(node)
def test_importfrom_stops_on_first_match():
# from foo import bar, baz, qux
src = "from foo import bar, baz, qux"
node = parse_first_importfrom(src)
analyzer = ImportAnalyzer({"baz", "qux"})
analyzer.visit_ImportFrom(node)
# 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-pr310-2025-06-10T01.40.46
Click to see suggested changes
if self.found_any_target_function: | |
return | |
if not node.module: | |
return | |
for alias in node.names: | |
if alias.name == "*": | |
continue | |
imported_name = alias.asname if alias.asname else alias.name | |
self.imported_names.add(imported_name) | |
if alias.name in self.function_names_to_find: | |
self.found_target_functions.add(alias.name) | |
# Check for qualified name matches | |
if node.module: | |
self.wildcard_modules.add(node.module) | |
else: | |
imported_name = alias.asname if alias.asname else alias.name | |
self.imported_modules.add(imported_name) | |
# Check for dynamic import functions | |
if node.module == "importlib" and alias.name == "import_module": | |
self.has_dynamic_imports = True | |
# Check if imported name is a target qualified name | |
if alias.name in self.function_names_to_find: | |
self.found_any_target_function = True | |
self.found_qualified_name = alias.name | |
return | |
# Check if module.name forms a target qualified name | |
qualified_name = f"{node.module}.{alias.name}" | |
if qualified_name in self.function_names_to_find: | |
self.found_target_functions.add(qualified_name) | |
self.generic_visit(node) | |
self.found_any_target_function = True | |
self.found_qualified_name = qualified_name | |
return | |
if self.found_any_target_function or not node.module: | |
return | |
module = node.module | |
target_functions = self.function_names_to_find | |
imported_modules = self.imported_modules | |
wildcard_modules = self.wildcard_modules | |
for alias in node.names: | |
alias_name = alias.name | |
if alias_name == "*": | |
wildcard_modules.add(module) | |
continue | |
imported_name = alias.asname or alias_name | |
imported_modules.add(imported_name) | |
# Fast detect dynamic import | |
if module == "importlib" and alias_name == "import_module": | |
self.has_dynamic_imports = True | |
# Check both short and qualified names using direct set membership | |
if alias_name in target_functions: | |
self.found_any_target_function = True | |
self.found_qualified_name = alias_name | |
return | |
qualified_name = f"{module}.{alias_name}" | |
if qualified_name in target_functions: | |
self.found_any_target_function = True | |
self.found_qualified_name = qualified_name | |
return | |
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 209% (2.09x) speedup for
ImportAnalyzer.visit_Import
incodeflash/discovery/discover_unit_tests.py
⏱️ Runtime :
521 microseconds
→169 microseconds
(best of357
runs)📝 Explanation and details
Here’s an optimized version of your `visit_Import` method. The main bottleneck is the **nested loop** at the end, which repeatedly checks every `target_func` in `function_names_to_find` for each module (O(M×N)). This can be reduced by **pre-indexing** your targets by prefix (the possible module), and **batching string manipulation** outside the loop.Below is the rewrite. I only updated the method; the rest of the class and comments are unchanged.
Changes explained:
__init__
, we precomputeself._module_prefix_map
so that for each unique module prefix (the part before.
), we map all targets starting with that prefix.visit_Import
, instead of iterating everytarget_func
for every module, we check if the module is a prefix inself._module_prefix_map
. No more O(M×N) lookups.No comments or function names were altered, only efficiency was improved.
✅ Correctness verification report:
🌀 Generated Regression Tests Details
To test or edit this optimization locally
git merge codeflash/optimize-pr310-2025-06-10T01.34.32
Click to see suggested changes