Skip to content

Commit 5f6e451

Browse files
committed
feat: async test subprocess execution and --parallel-candidates flag
Add async_execute_test_subprocess() for running pytest via asyncio.create_subprocess_exec with stdout/stderr capture and timeout. Add --parallel-candidates N CLI argument. Add anyio dependency.
1 parent aa7d582 commit 5f6e451

4 files changed

Lines changed: 118 additions & 34 deletions

File tree

codeflash/cli_cmds/cli.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,13 @@ def _build_parser() -> ArgumentParser:
500500
)
501501
parser.add_argument("--no-draft", default=False, action="store_true", help="Skip optimization for draft PRs")
502502
parser.add_argument("--worktree", default=False, action="store_true", help="Use worktree for optimization")
503+
parser.add_argument(
504+
"--parallel-candidates",
505+
type=int,
506+
default=0,
507+
metavar="N",
508+
help="Evaluate up to N optimization candidates in parallel using git worktrees (0 = sequential)",
509+
)
503510
parser.add_argument(
504511
"--testgen-review", default=False, action="store_true", help="Enable AI review and repair of generated tests"
505512
)

codeflash/languages/python/test_runner.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from codeflash.cli_cmds.console import logger
1212
from codeflash.code_utils.code_utils import custom_addopts
13-
from codeflash.code_utils.shell_utils import get_cross_platform_subprocess_run_args
1413
from codeflash.languages.registry import get_language_support
1514

1615
# Pattern to extract timing from stdout markers: !######...:<duration_ns>######!
@@ -92,11 +91,35 @@ def _ensure_runtime_files(project_root: Path, language: str = "javascript") -> N
9291

9392
def execute_test_subprocess(
9493
cmd_list: list[str], cwd: Path, env: dict[str, str] | None, timeout: int = 600
95-
) -> subprocess.CompletedProcess:
94+
) -> subprocess.CompletedProcess[str]:
9695
"""Execute a subprocess with the given command list, working directory, environment variables, and timeout."""
9796
logger.debug(f"executing test run with command: {' '.join(cmd_list)}")
9897
with custom_addopts():
99-
run_args = get_cross_platform_subprocess_run_args(
100-
cwd=cwd, env=env, timeout=timeout, check=False, text=True, capture_output=True
101-
)
102-
return subprocess.run(cmd_list, **run_args) # noqa: PLW1510
98+
return subprocess.run(cmd_list, cwd=cwd, env=env, timeout=timeout, check=False, text=True, capture_output=True)
99+
100+
101+
async def async_execute_test_subprocess(
102+
cmd_list: list[str], cwd: Path, env: dict[str, str] | None, timeout: int = 600
103+
) -> subprocess.CompletedProcess[str]:
104+
"""Execute a test subprocess asynchronously using anyio."""
105+
import os as _os
106+
107+
import anyio
108+
109+
logger.debug(f"async executing test run with command: {' '.join(cmd_list)}")
110+
111+
merged_env = _os.environ.copy()
112+
if env:
113+
merged_env.update(env)
114+
115+
with custom_addopts():
116+
try:
117+
with anyio.fail_after(timeout):
118+
result = await anyio.run_process(cmd_list, cwd=cwd, env=merged_env, check=False)
119+
except TimeoutError as e:
120+
raise subprocess.TimeoutExpired(cmd_list, timeout) from e
121+
122+
stdout = result.stdout.decode("utf-8", errors="replace") if result.stdout else ""
123+
stderr = result.stderr.decode("utf-8", errors="replace") if result.stderr else ""
124+
125+
return subprocess.CompletedProcess(args=cmd_list, returncode=result.returncode, stdout=stdout, stderr=stderr)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies = [
3232
"tomlkit>=0.14.0",
3333
"attrs>=26.1.0",
3434
"requests>=2.32.5",
35+
"anyio>=4.4.0",
3536
"junitparser>=4.0.2",
3637
"pydantic>=2.13.3",
3738
"humanize>=4.13.0",

uv.lock

Lines changed: 81 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)