Compare commits

...

4 Commits

Author SHA1 Message Date
Graham Neubig b59a774ee5 Merge branch 'main' into fix/ps1-metadata-escaping 2025-03-03 13:42:14 -05:00
openhands 25044442d0 fix: properly handle PS1 metadata JSON escaping
The JSON string in PS1 metadata doesn't need quote escaping because:
1. json.dumps() already produces valid JSON with properly escaped quotes
2. The JSON string is inside single quotes in PS1, so bash won't interpret backslashes

This fixes the double-escaping issue that was causing JSON parsing to fail.
2025-02-19 14:35:48 +00:00
openhands f7f43c4236 style: fix lint issues 2025-02-19 14:20:39 +00:00
openhands e87284bd9f fix: handle PS1 metadata JSON escaping properly
The PS1 metadata JSON string was being doubly escaped, causing JSON parsing
to fail. This was happening because we were escaping quotes with
json_str.replace('"', r'"') after json.dumps() had already properly
escaped them.

This fix:
1. Removes the unnecessary quote escaping since json.dumps() already handles that
2. Adds a test to prevent the double-escaping issue from recurring
2025-02-19 14:09:32 +00:00
2 changed files with 31 additions and 7 deletions
+3 -3
View File
@@ -45,9 +45,9 @@ class CmdOutputMetadata(BaseModel):
},
indent=2,
)
# Make sure we escape double quotes in the JSON string
# So that PS1 will keep them as part of the output
prompt += json_str.replace('"', r'\"')
# No need to escape quotes since json.dumps() already does that
# The JSON string will be inside single quotes in PS1, so bash won't interpret backslashes
prompt += json_str
prompt += CMD_OUTPUT_PS1_END + '\n' # Ensure there's a newline at the end
return prompt
+28 -4
View File
@@ -15,7 +15,11 @@ def test_ps1_metadata_format():
print(prompt)
assert prompt.startswith('\n###PS1JSON###\n')
assert prompt.endswith('\n###PS1END###\n')
assert r'\"exit_code\"' in prompt, 'PS1 prompt should contain escaped double quotes'
# The JSON string should be properly formatted with quotes but not escaped
assert '"exit_code"' in prompt, 'PS1 prompt should contain proper JSON with quotes'
# Shell variables should be present
assert r'\u' in prompt, 'PS1 prompt should contain shell variables'
assert r'\h' in prompt, 'PS1 prompt should contain shell variables'
def test_ps1_metadata_json_structure():
@@ -23,11 +27,9 @@ def test_ps1_metadata_json_structure():
prompt = CmdOutputMetadata.to_ps1_prompt()
# Extract JSON content between markers
json_str = prompt.replace('###PS1JSON###\n', '').replace('\n###PS1END###\n', '')
# Remove escaping before parsing
json_str = json_str.replace(r'\"', '"')
# Remove any trailing content after the JSON
json_str = json_str.split('###PS1END###')[0].strip()
data = json.loads(json_str)
data = json.loads(json_str) # Should parse without any unescaping needed
# Check required fields
expected_fields = {
@@ -40,6 +42,10 @@ def test_ps1_metadata_json_structure():
}
assert set(data.keys()) == expected_fields
# Check that shell variables are present
assert data['username'] == r'\u'
assert data['hostname'] == r'\h'
def test_ps1_metadata_parsing():
"""Test parsing PS1 output into CmdOutputMetadata"""
@@ -291,6 +297,24 @@ def test_cmd_output_observation_properties():
assert 'error' in str(obs)
def test_ps1_metadata_no_double_escaping():
"""Test that PS1 metadata JSON doesn't have doubly escaped quotes.
This test specifically checks for the issue where quotes were being
doubly escaped, causing JSON parsing to fail."""
prompt = CmdOutputMetadata.to_ps1_prompt()
# Extract the JSON part
json_str = prompt.replace('###PS1JSON###\n', '').replace('\n###PS1END###\n', '')
# The JSON string should be valid JSON without any escaping needed
data = json.loads(json_str)
assert isinstance(data, dict)
assert 'pid' in data
assert 'exit_code' in data
# The shell command should still have its quotes properly escaped
assert data['py_interpreter_path'].endswith('"")')
def test_ps1_metadata_empty_fields():
"""Test handling of empty fields in PS1 metadata"""
# Test with empty strings