Skip to content

Commit 82e9899

Browse files
authored
Chunk very long commands when writing file. (#247)
* Chunk very long commands when writing file. * Split tests in smaller ones
1 parent 7e3112e commit 82e9899

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

debug_gym/gym/workspace.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,25 @@ def write_file(self, filepath: str, content: str):
136136
"""Writes `content` to `filepath` exactly as-is, preserving any trailing newlines."""
137137
abs_filepath = self.resolve_path(filepath)
138138

139+
# We will split content in chunks of 32kB to avoid hitting command length limits.
140+
chunk_size = 32 * 1024 # 32kB
141+
first_chunk = content[:chunk_size]
142+
rest = content[chunk_size:]
143+
139144
# In the following command we:
140145
# - use a single-quoted heredoc (cat <<'nDEBUGGYM_EOF' ... nDEBUGGYM_EOF) so the heredoc body is taken literally (no shell expansion)
141146
# - append a sentinel character DEBUGGYM_DEL inside the heredoc so we can detect/restore trailing newlines later
142147
# - capture the heredoc output into shell variable CONTENT since command substitution strips trailing newlines
143148
# - "${CONTENT%DEBUGGYM_DEL}" removes the trailing sentinel DEBUGGYM_DEL (restoring the original trailing-newline state)
144149
# - echo -n writes the result without adding an extra newline
145-
cmd = f"CONTENT=$(cat <<'DEBUGGYM_EOF'\n{content}DEBUGGYM_DEL\nDEBUGGYM_EOF\n); echo -n \"${{CONTENT%DEBUGGYM_DEL}}\" > {abs_filepath}"
150+
cmd = f"CONTENT=$(cat <<'DEBUGGYM_EOF'\n{first_chunk}DEBUGGYM_DEL\nDEBUGGYM_EOF\n); echo -n \"${{CONTENT%DEBUGGYM_DEL}}\" > {abs_filepath}"
146151
self.terminal.run(cmd, raises=True)
147152

153+
for i in range(0, len(rest), chunk_size):
154+
chunk = rest[i : i + chunk_size]
155+
cmd = f"CONTENT=$(cat <<'DEBUGGYM_EOF'\n{chunk}DEBUGGYM_DEL\nDEBUGGYM_EOF\n); echo -n \"${{CONTENT%DEBUGGYM_DEL}}\" >> {abs_filepath}"
156+
self.terminal.run(cmd, raises=True)
157+
148158
def directory_tree(self, root: str | Path = None, max_depth: int = 1):
149159
root = self.resolve_path(root or self.working_dir, raises=True)
150160
# Use the terminal to run a bash command to list files

debug_gym/logger.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,12 @@ def __init__(
518518
):
519519
super().__init__(name)
520520
# If var env "DEBUG_GYM_DEBUG" is set, turn on debug mode
521-
if os.environ.get("DEBUG_GYM_DEBUG"):
521+
if os.environ.get("DEBUG_GYM_DEBUG", "").strip().lower() in (
522+
"1",
523+
"true",
524+
"yes",
525+
"on",
526+
):
522527
level = logging.DEBUG
523528

524529
# Prevent the log messages from being propagated to the root logger

tests/gym/test_workspace.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,23 +292,43 @@ def test_read_file_raises_for_nonexistent_file(workspace):
292292
workspace.read_file("/test.txt")
293293

294294

295-
def test_write_file(workspace):
295+
def test_write_file_basic(workspace):
296296
file_path = workspace.working_dir / "test.txt"
297297
file_content = "Hello, DebugGym!\n\n\n"
298298
workspace.write_file("test.txt", file_content)
299299
assert file_path.read_text() == file_content
300300

301-
# Test with single line, no newline at the end.
301+
302+
def test_write_file_single_line_no_newline(workspace):
303+
file_path = workspace.working_dir / "test.txt"
302304
file_content_single_line = "Hello, DebugGym!"
303305
workspace.write_file("test.txt", file_content_single_line)
304306
assert file_path.read_text() == file_content_single_line
305307

306-
# Should still work if the content ends with the delimiter.
308+
309+
def test_write_file_with_delimiter(workspace):
310+
file_path = workspace.working_dir / "test.txt"
307311
file_content_single_line = "Hello, DebugGym!nDEBUGGYM_DEL"
308312
workspace.write_file("test.txt", file_content_single_line)
309313
assert file_path.read_text() == file_content_single_line
310314

311-
# Test with newlines
315+
316+
def test_write_file_with_newlines(workspace):
317+
file_path = workspace.working_dir / "test.txt"
312318
file_content_with_newlines = "Hello, DebugGym!\nThis is a test.\n"
313319
workspace.write_file("test.txt", file_content_with_newlines)
314320
assert file_path.read_text() == file_content_with_newlines
321+
322+
323+
def test_write_file_empty_content(workspace):
324+
file_path = workspace.working_dir / "test.txt"
325+
file_content_empty = ""
326+
workspace.write_file("test.txt", file_content_empty)
327+
assert file_path.read_text() == file_content_empty
328+
329+
330+
def test_write_file_exceeding_max_command_length(workspace):
331+
file_path = workspace.working_dir / "test.txt"
332+
file_content_exceeding_max_command_length = "A" * (2 * 1024**2) # 2MB of 'A's
333+
workspace.write_file("test.txt", file_content_exceeding_max_command_length)
334+
assert file_path.read_text() == file_content_exceeding_max_command_length

0 commit comments

Comments
 (0)