Skip to content

Commit ee38b2a

Browse files
davidkoppclaude
andcommitted
Add command execution timing when debug is enabled
- Add time.perf_counter() measurements to both HostExecutor and DockerExecutor - Only capture timing when debug mode is enabled to avoid performance overhead - Display execution time with 3 decimal precision for successful commands, timeouts, and failures - Support timing for both normal execution and Docker fallback direct execution 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 6baafcd commit ee38b2a

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

dependency_resolver/executors/docker_executor.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import shlex
2+
import time
23
from typing import Optional
34

45
from ..core.interfaces import EnvironmentExecutor
@@ -48,8 +49,11 @@ def execute_command(self, command: str, working_dir: Optional[str] = None) -> tu
4849
Returns actual command exit code on success, or 1 for execution environment failures.
4950
"""
5051
if self.debug:
52+
start_time = time.perf_counter()
5153
workdir_info = f" (workdir: {working_dir})" if working_dir else ""
5254
print(f"Executing docker command: sh -c '{command}'{workdir_info}")
55+
else:
56+
start_time = None
5357

5458
try:
5559
# First, try with sh
@@ -59,22 +63,34 @@ def execute_command(self, command: str, working_dir: Optional[str] = None) -> tu
5963
stdout = result.output.decode("utf-8") if result.output else ""
6064
stderr = ""
6165

62-
if self.debug:
63-
print(f"Docker command exit code: {result.exit_code}")
66+
if self.debug and start_time is not None:
67+
elapsed_time = time.perf_counter() - start_time
68+
print(f"Docker command completed in {elapsed_time:.3f}s with exit code: {result.exit_code}")
6469

6570
return stdout, stderr, result.exit_code
6671

6772
except docker.errors.APIError as e:
6873
# Check if this is a "sh not found" error
6974
if "executable file not found" in str(e).lower() and "sh" in str(e).lower():
70-
return self._execute_command_direct(command, working_dir)
75+
return self._execute_command_direct(command, working_dir, start_time)
7176
else:
77+
if self.debug and start_time is not None:
78+
elapsed_time = time.perf_counter() - start_time
79+
print(f"Docker command failed after {elapsed_time:.3f}s: {str(e)}")
7280
return "", f"Docker API error: {str(e)}", 1
7381
except (OSError, ValueError) as e:
82+
if self.debug and start_time is not None:
83+
elapsed_time = time.perf_counter() - start_time
84+
print(f"Docker command failed after {elapsed_time:.3f}s: {str(e)}")
7485
return "", f"Command execution failed: {str(e)}", 1
7586

76-
def _execute_command_direct(self, command: str, working_dir: Optional[str] = None) -> tuple[str, str, int]:
87+
def _execute_command_direct(
88+
self, command: str, working_dir: Optional[str] = None, start_time: Optional[float] = None
89+
) -> tuple[str, str, int]:
7790
"""Fallback: execute simple commands directly without shell."""
91+
if start_time is None:
92+
start_time = time.perf_counter()
93+
7894
if self.debug:
7995
workdir_info = f" (workdir: {working_dir})" if working_dir else ""
8096
print(f"Fallback: executing docker command directly: {command}{workdir_info}")
@@ -83,6 +99,9 @@ def _execute_command_direct(self, command: str, working_dir: Optional[str] = Non
8399
# Handle only the simple cases we actually use
84100
cmd_parts = DockerExecutor._parse_simple_command(command)
85101
if not cmd_parts:
102+
if self.debug:
103+
elapsed_time = time.perf_counter() - start_time
104+
print(f"Direct execution failed after {elapsed_time:.3f}s: command too complex")
86105
return "", f"Command too complex for direct execution (no shell available): {command}", 1
87106

88107
if self.debug:
@@ -93,11 +112,15 @@ def _execute_command_direct(self, command: str, working_dir: Optional[str] = Non
93112
stderr = ""
94113

95114
if self.debug:
96-
print(f"Direct execution exit code: {result.exit_code}")
115+
elapsed_time = time.perf_counter() - start_time
116+
print(f"Direct execution completed in {elapsed_time:.3f}s with exit code: {result.exit_code}")
97117

98118
return stdout, stderr, result.exit_code
99119

100120
except docker.errors.APIError as e:
121+
if self.debug:
122+
elapsed_time = time.perf_counter() - start_time
123+
print(f"Direct execution failed after {elapsed_time:.3f}s: {str(e)}")
101124
return "", f"Direct execution failed: {str(e)}", 1
102125

103126
@staticmethod

dependency_resolver/executors/host_executor.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import subprocess
3+
import time
34

45
from ..core.interfaces import EnvironmentExecutor
56
from typing import Optional
@@ -17,6 +18,13 @@ def execute_command(self, command: str, working_dir: Optional[str] = None) -> tu
1718
1819
Returns actual command exit code on success, or 1 for execution environment failures.
1920
"""
21+
if self.debug:
22+
start_time = time.perf_counter()
23+
workdir_info = f" (workdir: {working_dir})" if working_dir else ""
24+
print(f"Executing host command: {command}{workdir_info}")
25+
else:
26+
start_time = None
27+
2028
try:
2129
result = subprocess.run(
2230
command,
@@ -27,10 +35,21 @@ def execute_command(self, command: str, working_dir: Optional[str] = None) -> tu
2735
timeout=30,
2836
check=False,
2937
)
38+
39+
if self.debug and start_time is not None:
40+
elapsed_time = time.perf_counter() - start_time
41+
print(f"Host command completed in {elapsed_time:.3f}s with exit code: {result.returncode}")
42+
3043
return result.stdout, result.stderr, result.returncode
3144
except subprocess.TimeoutExpired:
45+
if self.debug and start_time is not None:
46+
elapsed_time = time.perf_counter() - start_time
47+
print(f"Host command timed out after {elapsed_time:.3f}s")
3248
return "", "Command timed out after 30 seconds", 1
3349
except (subprocess.SubprocessError, OSError) as e:
50+
if self.debug and start_time is not None:
51+
elapsed_time = time.perf_counter() - start_time
52+
print(f"Host command failed after {elapsed_time:.3f}s: {str(e)}")
3453
return "", f"Command execution failed: {str(e)}", 1
3554

3655
def path_exists(self, path: str) -> bool:

0 commit comments

Comments
 (0)