Skip to content

Commit d571512

Browse files
authored
Merge branch 'main' into jit-docs
2 parents 2dcfba6 + 7bf9f99 commit d571512

5 files changed

Lines changed: 119 additions & 6 deletions

File tree

codeflash/api/cfapi.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ def suggest_changes(
171171
replay_tests: str = "",
172172
concolic_tests: str = "",
173173
optimization_review: str = "",
174+
original_line_profiler: str | None = None,
175+
optimized_line_profiler: str | None = None,
174176
) -> Response:
175177
"""Suggest changes to a pull request.
176178
@@ -182,6 +184,8 @@ def suggest_changes(
182184
:param file_changes: A dictionary of file changes.
183185
:param pr_comment: The pull request comment object, containing the optimization explanation, best runtime, etc.
184186
:param generated_tests: The generated tests.
187+
:param original_line_profiler: Line profiler results for original code (markdown format).
188+
:param optimized_line_profiler: Line profiler results for optimized code (markdown format).
185189
:return: The response object.
186190
"""
187191
payload = {
@@ -197,7 +201,10 @@ def suggest_changes(
197201
"replayTests": replay_tests,
198202
"concolicTests": concolic_tests,
199203
"optimizationReview": optimization_review, # impact keyword left for legacy reasons, touches js/ts code
204+
"originalLineProfiler": original_line_profiler,
205+
"optimizedLineProfiler": optimized_line_profiler,
200206
}
207+
201208
return make_cfapi_request(endpoint="/suggest-pr-changes", method="POST", payload=payload)
202209

203210

@@ -214,6 +221,8 @@ def create_pr(
214221
replay_tests: str = "",
215222
concolic_tests: str = "",
216223
optimization_review: str = "",
224+
original_line_profiler: str | None = None,
225+
optimized_line_profiler: str | None = None,
217226
) -> Response:
218227
"""Create a pull request, targeting the specified branch. (usually 'main').
219228
@@ -223,6 +232,8 @@ def create_pr(
223232
:param file_changes: A dictionary of file changes.
224233
:param pr_comment: The pull request comment object, containing the optimization explanation, best runtime, etc.
225234
:param generated_tests: The generated tests.
235+
:param original_line_profiler: Line profiler results for original code (markdown format).
236+
:param optimized_line_profiler: Line profiler results for optimized code (markdown format).
226237
:return: The response object.
227238
"""
228239
# convert Path objects to strings
@@ -239,7 +250,10 @@ def create_pr(
239250
"replayTests": replay_tests,
240251
"concolicTests": concolic_tests,
241252
"optimizationReview": optimization_review, # Impact keyword left for legacy reasons, it touches js/ts codebase
253+
"originalLineProfiler": original_line_profiler,
254+
"optimizedLineProfiler": optimized_line_profiler,
242255
}
256+
243257
return make_cfapi_request(endpoint="/create-pr", method="POST", payload=payload)
244258

245259

@@ -269,6 +283,8 @@ def create_staging(
269283
concolic_tests: str,
270284
root_dir: Path,
271285
optimization_review: str = "",
286+
original_line_profiler: str | None = None,
287+
optimized_line_profiler: str | None = None,
272288
) -> Response:
273289
"""Create a staging pull request, targeting the specified branch. (usually 'staging').
274290
@@ -279,6 +295,8 @@ def create_staging(
279295
:param generated_original_test_source: Generated tests for the original function.
280296
:param function_trace_id: Unique identifier for this optimization trace.
281297
:param coverage_message: Coverage report or summary.
298+
:param original_line_profiler: Line profiler results for original code (markdown format).
299+
:param optimized_line_profiler: Line profiler results for optimized code (markdown format).
282300
:return: The response object from the backend.
283301
"""
284302
relative_path = explanation.file_path.relative_to(root_dir).as_posix()
@@ -310,6 +328,8 @@ def create_staging(
310328
"replayTests": replay_tests,
311329
"concolicTests": concolic_tests,
312330
"optimizationReview": optimization_review, # Impact keyword left for legacy reasons, it touches js/ts codebase
331+
"originalLineProfiler": original_line_profiler,
332+
"optimizedLineProfiler": optimized_line_profiler,
313333
}
314334

315335
return make_cfapi_request(endpoint="/create-staging", method="POST", payload=payload)

codeflash/optimization/function_optimizer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,8 @@ def process_review(
20012001
"coverage_message": coverage_message,
20022002
"replay_tests": replay_tests,
20032003
"concolic_tests": concolic_tests,
2004+
"original_line_profiler": original_code_baseline.line_profile_results.get("str_out", ""),
2005+
"optimized_line_profiler": best_optimization.line_profiler_test_results.get("str_out", ""),
20042006
}
20052007

20062008
raise_pr = not self.args.no_pr

codeflash/result/create_pr.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ def check_create_pr(
186186
root_dir: Path,
187187
git_remote: Optional[str] = None,
188188
optimization_review: str = "",
189+
original_line_profiler: str | None = None,
190+
optimized_line_profiler: str | None = None,
189191
) -> None:
190192
pr_number: Optional[int] = env_utils.get_pr_number()
191193
git_repo = git.Repo(search_parent_directories=True)
@@ -230,6 +232,8 @@ def check_create_pr(
230232
replay_tests=replay_tests,
231233
concolic_tests=concolic_tests,
232234
optimization_review=optimization_review,
235+
original_line_profiler=original_line_profiler,
236+
optimized_line_profiler=optimized_line_profiler,
233237
)
234238
if response.ok:
235239
logger.info(f"Suggestions were successfully made to PR #{pr_number}")
@@ -282,6 +286,8 @@ def check_create_pr(
282286
replay_tests=replay_tests,
283287
concolic_tests=concolic_tests,
284288
optimization_review=optimization_review,
289+
original_line_profiler=original_line_profiler,
290+
optimized_line_profiler=optimized_line_profiler,
285291
)
286292
if response.ok:
287293
pr_id = response.text

codeflash/verification/comparator.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,25 @@
3030
# These paths vary between test runs but are logically equivalent
3131
PYTEST_TEMP_PATH_PATTERN = re.compile(r"/tmp/pytest-of-[^/]+/pytest-\d+/") # noqa: S108
3232

33+
# Pattern to match Python tempfile directories: /tmp/tmp<random>/
34+
# Created by tempfile.mkdtemp() or tempfile.TemporaryDirectory()
35+
PYTHON_TEMPFILE_PATTERN = re.compile(r"/tmp/tmp[a-zA-Z0-9_]+/") # noqa: S108
36+
3337

3438
def _normalize_temp_path(path: str) -> str:
3539
"""Normalize temporary file paths by replacing session-specific components.
3640
37-
Pytest creates temp directories like /tmp/pytest-of-<user>/pytest-<N>/
38-
where N is a session number that increments. When comparing return values
39-
from different test runs, these paths should be considered equivalent.
41+
Handles two types of temp paths:
42+
- Pytest: /tmp/pytest-of-<user>/pytest-<N>/ -> /tmp/pytest-temp/
43+
- Python tempfile: /tmp/tmp<random>/ -> /tmp/python-temp/
4044
"""
41-
return PYTEST_TEMP_PATH_PATTERN.sub("/tmp/pytest-temp/", path) # noqa: S108
45+
path = PYTEST_TEMP_PATH_PATTERN.sub("/tmp/pytest-temp/", path) # noqa: S108
46+
return PYTHON_TEMPFILE_PATTERN.sub("/tmp/python-temp/", path) # noqa: S108
4247

4348

4449
def _is_temp_path(s: str) -> bool:
45-
"""Check if a string looks like a pytest temp path."""
46-
return PYTEST_TEMP_PATH_PATTERN.search(s) is not None
50+
"""Check if a string looks like a temp path (pytest or Python tempfile)."""
51+
return PYTEST_TEMP_PATH_PATTERN.search(s) is not None or PYTHON_TEMPFILE_PATTERN.search(s) is not None
4752

4853

4954
def _extract_exception_from_message(msg: str) -> Optional[BaseException]: # noqa: FA100

tests/test_comparator.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from codeflash.models.models import FunctionTestInvocation, InvocationId, TestResults, TestType
2020
from codeflash.verification.comparator import (
2121
PYTEST_TEMP_PATH_PATTERN,
22+
PYTHON_TEMPFILE_PATTERN,
2223
_extract_exception_from_message,
2324
_get_wrapped_exception,
2425
_is_temp_path,
@@ -3977,3 +3978,82 @@ def test_config_object_with_paths(self):
39773978
"permanent_dir": "/home/user/data/"
39783979
}
39793980
assert comparator(config1, config2)
3981+
3982+
3983+
class TestPythonTempfilePaths:
3984+
"""Tests for Python tempfile paths (from tempfile.mkdtemp() or TemporaryDirectory())."""
3985+
3986+
def test_is_temp_path_detects_python_tempfile(self):
3987+
"""Test that _is_temp_path detects Python tempfile paths."""
3988+
assert _is_temp_path("/tmp/tmpqtwy7hpf/special.txt")
3989+
assert _is_temp_path("/tmp/tmpp6wx3tz3/special.txt")
3990+
assert _is_temp_path("/tmp/tmpabcdef12/")
3991+
assert _is_temp_path("/tmp/tmp_underscore/file.txt")
3992+
3993+
def test_is_temp_path_various_tempfile_names(self):
3994+
"""Test various tempfile naming patterns."""
3995+
assert _is_temp_path("/tmp/tmpABCDEF/file.txt") # uppercase
3996+
assert _is_temp_path("/tmp/tmp123456/file.txt") # numeric
3997+
assert _is_temp_path("/tmp/tmpaBc123/file.txt") # mixed
3998+
assert _is_temp_path("/tmp/tmp_test_dir/subdir/file.txt") # with underscore
3999+
4000+
def test_is_temp_path_non_tempfile(self):
4001+
"""Test that non-tempfile paths are not detected."""
4002+
assert not _is_temp_path("/tmp/mydir/file.txt") # doesn't start with tmp
4003+
assert not _is_temp_path("/tmp/temp/file.txt") # temp, not tmp
4004+
assert not _is_temp_path("/home/user/tmp123/file.txt") # not in /tmp/
4005+
4006+
def test_normalize_temp_path_python_tempfile(self):
4007+
"""Test normalization of Python tempfile paths."""
4008+
path1 = _normalize_temp_path("/tmp/tmpqtwy7hpf/special.txt")
4009+
path2 = _normalize_temp_path("/tmp/tmpp6wx3tz3/special.txt")
4010+
assert path1 == path2 == "/tmp/python-temp/special.txt"
4011+
4012+
def test_normalize_temp_path_preserves_subdirs(self):
4013+
"""Test that subdirectories are preserved during normalization."""
4014+
result = _normalize_temp_path("/tmp/tmpabcdef12/subdir/nested/file.txt")
4015+
assert result == "/tmp/python-temp/subdir/nested/file.txt"
4016+
4017+
def test_comparator_python_tempfile_paths_equal(self):
4018+
"""Test that different tempfile paths with same content are equal."""
4019+
path1 = "/tmp/tmpqtwy7hpf/special.txt"
4020+
path2 = "/tmp/tmpp6wx3tz3/special.txt"
4021+
assert comparator(path1, path2)
4022+
4023+
def test_comparator_python_tempfile_different_filenames_not_equal(self):
4024+
"""Test that different filenames in tempfile paths are not equal."""
4025+
path1 = "/tmp/tmpqtwy7hpf/special.txt"
4026+
path2 = "/tmp/tmpp6wx3tz3/different.txt"
4027+
assert not comparator(path1, path2)
4028+
4029+
def test_comparator_python_tempfile_in_tuple(self):
4030+
"""Test tempfile paths in tuples."""
4031+
orig = ("/tmp/tmpqtwy7hpf/special.txt",)
4032+
new = ("/tmp/tmpp6wx3tz3/special.txt",)
4033+
assert comparator(orig, new)
4034+
4035+
def test_comparator_python_tempfile_in_list(self):
4036+
"""Test tempfile paths in lists."""
4037+
orig = ["/tmp/tmpabcdef12/file1.txt", "/tmp/tmpabcdef12/file2.txt"]
4038+
new = ["/tmp/tmpxyz78901/file1.txt", "/tmp/tmpxyz78901/file2.txt"]
4039+
assert comparator(orig, new)
4040+
4041+
def test_comparator_python_tempfile_in_dict(self):
4042+
"""Test tempfile paths in dictionaries."""
4043+
orig = {"output": "/tmp/tmpabcdef12/result.json"}
4044+
new = {"output": "/tmp/tmpxyz78901/result.json"}
4045+
assert comparator(orig, new)
4046+
4047+
def test_comparator_mixed_pytest_and_python_tempfile(self):
4048+
"""Test that pytest and Python tempfile paths don't match each other."""
4049+
pytest_path = "/tmp/pytest-of-user/pytest-0/file.txt"
4050+
python_path = "/tmp/tmpabcdef12/file.txt"
4051+
# These should not be equal - they're different temp path types
4052+
assert not comparator(pytest_path, python_path)
4053+
4054+
def test_python_tempfile_pattern_regex(self):
4055+
"""Test the PYTHON_TEMPFILE_PATTERN regex directly."""
4056+
assert PYTHON_TEMPFILE_PATTERN.search("/tmp/tmpabcdef/file.txt")
4057+
assert PYTHON_TEMPFILE_PATTERN.search("/tmp/tmp123456/")
4058+
assert not PYTHON_TEMPFILE_PATTERN.search("/tmp/mydir/file.txt")
4059+
assert not PYTHON_TEMPFILE_PATTERN.search("/home/tmp123/file.txt")

0 commit comments

Comments
 (0)