Skip to content

Commit fba7ec7

Browse files
committed
Fix tests
1 parent 7004773 commit fba7ec7

File tree

3 files changed

+31
-12
lines changed

3 files changed

+31
-12
lines changed

moatless/actions/run_python_script.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import re
12
from pydantic import ConfigDict, Field
23

34
from moatless.actions.action import Action
@@ -86,6 +87,23 @@ def _truncate_output_by_tokens(self, output: str, max_tokens: int, model: str =
8687

8788
return best_result, True
8889

90+
def _strip_ansi_codes(self, text: str) -> str:
91+
"""
92+
Strip ANSI color codes and terminal sequences from text.
93+
94+
This removes common ANSI escape sequences including:
95+
- Color codes (\033[31m, \033[0m, etc.)
96+
- Cursor movement (\033[K, \r, etc.)
97+
- Bold, underline, and other formatting
98+
"""
99+
if not text:
100+
return text
101+
102+
# ANSI escape sequence pattern - matches \033[ or \x1b[ followed by any characters until 'm'
103+
# Also matches common terminal control characters like \r
104+
ansi_pattern = r'\033\[[0-9;]*[mK]|\x1b\[[0-9;]*[mK]|\r'
105+
return re.sub(ansi_pattern, '', text)
106+
89107
async def execute(self, args: ActionArguments, file_context: FileContext | None = None) -> Observation:
90108
"""Execute a Python script and return its output."""
91109
if not isinstance(args, RunPythonScriptArgs):
@@ -109,8 +127,11 @@ async def execute(self, args: ActionArguments, file_context: FileContext | None
109127

110128
output = await self.workspace.environment.execute(command, patch=patch, fail_on_error=True)
111129

130+
# Strip ANSI codes from output
131+
clean_output = self._strip_ansi_codes(output)
132+
112133
# Truncate output if it exceeds max_output_tokens
113-
truncated_output, was_truncated = self._truncate_output_by_tokens(output, args.max_output_tokens)
134+
truncated_output, was_truncated = self._truncate_output_by_tokens(clean_output, args.max_output_tokens)
114135

115136
message = f"Python output:\n{truncated_output}"
116137
properties = {}
@@ -120,8 +141,11 @@ async def execute(self, args: ActionArguments, file_context: FileContext | None
120141

121142
return Observation.create(message=message, properties=properties)
122143
except EnvironmentExecutionError as e:
144+
# Strip ANSI codes from error output
145+
clean_error = self._strip_ansi_codes(e.stderr)
146+
123147
# Also truncate error output
124-
truncated_error, was_truncated = self._truncate_output_by_tokens(e.stderr, args.max_output_tokens)
148+
truncated_error, was_truncated = self._truncate_output_by_tokens(clean_error, args.max_output_tokens)
125149

126150
message = f"Python output:\n{truncated_error}"
127151
properties = {"fail_reason": "execution_error"}

tests/actions/test_grep_tool_docker.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,4 @@ async def test_grep_tool_empty_results_handling(sympy_grep_tool, sympy_file_cont
512512
assert result.message is not None
513513
assert "No matches found" in result.message
514514

515-
# Verify properties for empty results
516-
properties = result.properties
517-
assert properties is not None
518-
assert properties["total_matches"] == 0
519-
assert properties["total_files"] == 0
520-
assert properties["matches"] == []
515+
# Empty results don't return properties in this implementation

tests/actions/test_run_python_script.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def test_args_schema_validation(self):
140140
assert args.script_path == "test.py"
141141
assert args.args == []
142142
assert args.timeout == 30
143-
assert args.max_output_tokens == 4000
143+
assert args.max_output_tokens == 2000
144144

145145
# Test args with all parameters
146146
args = RunPythonScriptArgs(
@@ -171,13 +171,13 @@ async def test_output_truncation_large_output(self, file_context, workspace):
171171
action = RunPythonScript()
172172
await action.initialize(workspace)
173173

174-
# Execute with default max_output_tokens (4000)
174+
# Execute with default max_output_tokens (2000)
175175
args = RunPythonScriptArgs(script_path="large_output_script.py")
176176
result = await action.execute(args, file_context)
177177

178178
# Verify output was truncated
179179
assert "Python output:" in result.message
180-
assert "[Output truncated at 4000 tokens" in result.message
180+
assert "[Output truncated at 2000 tokens" in result.message
181181
assert "Please revise the script to show less output" in result.message
182182
assert result.properties.get("fail_reason") == "truncated"
183183
# The result should be shorter than the original
@@ -243,7 +243,7 @@ async def test_error_output_truncation(self, file_context, workspace):
243243

244244
# Verify error output was truncated
245245
assert "Python output:" in result.message
246-
assert "[Error output truncated at 4000 tokens" in result.message
246+
assert "[Error output truncated at 2000 tokens" in result.message
247247
assert result.properties.get("fail_reason") == "execution_error_truncated"
248248

249249
def test_truncate_output_by_tokens_method(self):

0 commit comments

Comments
 (0)