2025-10-11 07:04:59 +00:00
|
|
|
import tempfile
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
2026-02-19 08:21:34 +00:00
|
|
|
from codeflash.languages.python.static_analysis.code_extractor import add_needed_imports_from_module
|
2025-10-11 07:04:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_add_needed_imports_with_none_aliases():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
import json
|
|
|
|
|
from typing import Dict as MyDict, Optional
|
|
|
|
|
from collections import defaultdict
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def target_function():
|
|
|
|
|
pass
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
expected_output = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def target_function():
|
|
|
|
|
pass
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_add_needed_imports_complex_aliases():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
import os
|
|
|
|
|
import sys as system
|
|
|
|
|
from typing import Dict, List as MyList, Optional as Opt
|
|
|
|
|
from collections import defaultdict as dd, Counter
|
|
|
|
|
from pathlib import Path
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def my_function():
|
|
|
|
|
return "test"
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
expected_output = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def my_function():
|
|
|
|
|
return "test"
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_add_needed_imports_with_usage():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
import json
|
|
|
|
|
from typing import Dict as MyDict, Optional
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def target_function():
|
|
|
|
|
data = json.loads('{"key": "value"}')
|
|
|
|
|
my_dict: MyDict[str, str] = {}
|
|
|
|
|
opt_value: Optional[str] = None
|
|
|
|
|
dd = defaultdict(list)
|
|
|
|
|
return data, my_dict, opt_value, dd
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
expected_output = """import json
|
2025-10-11 07:04:59 +00:00
|
|
|
from typing import Dict as MyDict, Optional
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
def target_function():
|
|
|
|
|
data = json.loads('{"key": "value"}')
|
|
|
|
|
my_dict: MyDict[str, str] = {}
|
|
|
|
|
opt_value: Optional[str] = None
|
|
|
|
|
dd = defaultdict(list)
|
|
|
|
|
return data, my_dict, opt_value, dd
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
# Assert exact expected output
|
|
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_litellm_router_style_imports():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
import asyncio
|
|
|
|
|
import copy
|
|
|
|
|
import json
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
from typing import Dict, List, Optional, Union
|
|
|
|
|
from litellm.types.utils import ModelInfo
|
|
|
|
|
from litellm.types.utils import ModelInfo as ModelMapInfo
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
target_code = '''
|
|
|
|
|
def target_function():
|
|
|
|
|
"""Target function for testing."""
|
|
|
|
|
pass
|
|
|
|
|
'''
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
expected_output = '''
|
|
|
|
|
def target_function():
|
|
|
|
|
"""Target function for testing."""
|
|
|
|
|
pass
|
|
|
|
|
'''
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "complex_source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_edge_case_none_values_in_alias_pairs():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
from typing import Dict as MyDict, List, Optional as Opt
|
|
|
|
|
from collections import defaultdict, Counter as cnt
|
|
|
|
|
from pathlib import Path
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def my_test_function():
|
|
|
|
|
return "test"
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
expected_output = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def my_test_function():
|
|
|
|
|
return "test"
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "edge_case_source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_partial_import_usage():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
from typing import Dict, List, Optional
|
|
|
|
|
from collections import defaultdict, Counter
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def use_some_imports():
|
|
|
|
|
path = os.path.join("a", "b")
|
|
|
|
|
my_dict: Dict[str, int] = {}
|
|
|
|
|
counter = Counter([1, 2, 3])
|
|
|
|
|
return path, my_dict, counter
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
expected_output = """import os
|
2025-10-11 07:04:59 +00:00
|
|
|
from collections import Counter
|
|
|
|
|
from typing import Dict
|
|
|
|
|
|
|
|
|
|
def use_some_imports():
|
|
|
|
|
path = os.path.join("a", "b")
|
|
|
|
|
my_dict: Dict[str, int] = {}
|
|
|
|
|
counter = Counter([1, 2, 3])
|
|
|
|
|
return path, my_dict, counter
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_alias_handling():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
from typing import Dict as MyDict, List as MyList, Optional
|
|
|
|
|
from collections import defaultdict as dd, Counter
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def test_aliases():
|
|
|
|
|
d: MyDict[str, int] = {}
|
|
|
|
|
lst: MyList[str] = []
|
|
|
|
|
dd_instance = dd(list)
|
|
|
|
|
return d, lst, dd_instance
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
expected_output = """from collections import defaultdict as dd
|
2025-10-11 07:04:59 +00:00
|
|
|
from typing import Dict as MyDict, List as MyList
|
|
|
|
|
|
|
|
|
|
def test_aliases():
|
|
|
|
|
d: MyDict[str, int] = {}
|
|
|
|
|
lst: MyList[str] = []
|
|
|
|
|
dd_instance = dd(list)
|
|
|
|
|
return d, lst, dd_instance
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
assert result.strip() == expected_output.strip()
|
|
|
|
|
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
def test_add_needed_imports_with_nonealiases():
|
2026-01-29 09:39:48 +00:00
|
|
|
source_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
import json
|
|
|
|
|
from typing import Dict as MyDict, Optional
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
target_code = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def target_function():
|
|
|
|
|
pass
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
temp_path = Path(temp_dir)
|
|
|
|
|
src_path = temp_path / "source.py"
|
|
|
|
|
dst_path = temp_path / "target.py"
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
src_path.write_text(source_code)
|
|
|
|
|
dst_path.write_text(target_code)
|
2026-01-29 09:39:48 +00:00
|
|
|
|
2025-10-11 07:04:59 +00:00
|
|
|
# This should not raise a TypeError
|
|
|
|
|
result = add_needed_imports_from_module(
|
|
|
|
|
src_module_code=source_code,
|
|
|
|
|
dst_module_code=target_code,
|
|
|
|
|
src_path=src_path,
|
|
|
|
|
dst_path=dst_path,
|
2026-01-29 09:39:48 +00:00
|
|
|
project_root=temp_path,
|
2025-10-11 07:04:59 +00:00
|
|
|
)
|
|
|
|
|
|
2026-01-29 09:39:48 +00:00
|
|
|
expected_output = """
|
2025-10-11 07:04:59 +00:00
|
|
|
def target_function():
|
|
|
|
|
pass
|
2026-01-29 09:39:48 +00:00
|
|
|
"""
|
|
|
|
|
assert result.strip() == expected_output.strip()
|