Skip to main content

What You’ll Learn

  • How to upload a multi-file Python project into a sandbox to simulate a repository
  • How to run a test suite before and after a fix to demonstrate a regression cycle
  • How to apply a targeted code fix by reading, patching, and writing a file
  • How to show a simple diff of the change inside the sandbox

Prerequisites

  • Declaw running locally or in the cloud (see Deployment)
  • DECLAW_API_KEY and DECLAW_DOMAIN set in your environment
This example is available in Python. TypeScript support coming soon.

Code Walkthrough

1. The buggy repository

The simulated repository contains two modules and a test file. string_utils.py has a deliberate bug — the count_vowels function is missing 'u' from the vowel set:
STRING_UTILS = """\
def count_vowels(s: str) -> int:
    \"\"\"Count the number of vowels in a string.\"\"\"
    # BUG: missing 'u' from vowels
    return sum(1 for c in s.lower() if c in "aeio")
"""

MATH_UTILS = """\
def factorial(n: int) -> int:
    if n < 0:
        raise ValueError("n must be non-negative")
    if n <= 1:
        return 1
    return n * factorial(n - 1)

def fibonacci(n: int) -> list:
    if n <= 0: return []
    if n == 1: return [0]
    seq = [0, 1]
    while len(seq) < n:
        seq.append(seq[-1] + seq[-2])
    return seq
"""
The test file has cases that will fail due to the bug:
TEST_FILE = """\
class TestStringUtils(unittest.TestCase):

    def test_count_vowels(self):
        self.assertEqual(count_vowels("hello"), 2)
        self.assertEqual(count_vowels("AEIOU"), 5)  # Will fail: counts 4, not 5
        self.assertEqual(count_vowels("ubuntu"), 3)  # Will fail: counts 2, not 3
"""

2. Upload and run tests before the fix

from declaw import Sandbox

sbx = Sandbox.create(template="python", timeout=300)
try:
    sbx.files.write("/home/user/repo/string_utils.py", STRING_UTILS)
    sbx.files.write("/home/user/repo/math_utils.py", MATH_UTILS)
    sbx.files.write("/home/user/repo/test_all.py", TEST_FILE)

    result_before = sbx.commands.run(
        "cd /home/user/repo && python3 -m unittest test_all -v 2>&1"
    )
    print(result_before.stdout)
    print(f"Exit code (before): {result_before.exit_code}")  # 1 (failure)

3. Apply the bug fix

The fix script reads the file, applies a targeted string replacement, and writes it back:
FIX_SCRIPT = """\
with open("/home/user/repo/string_utils.py", "r") as f:
    content = f.read()

# Save original for diff
with open("/home/user/repo/string_utils.py.orig", "w") as f:
    f.write(content)

# Apply the fix: add missing 'u' to vowels string
fixed = content.replace(
    'return sum(1 for c in s.lower() if c in "aeio")',
    'return sum(1 for c in s.lower() if c in "aeiou")'
)

with open("/home/user/repo/string_utils.py", "w") as f:
    f.write(fixed)

print("Fix applied: added 'u' to vowels in count_vowels()")
"""

sbx.files.write("/home/user/repo/fix.py", FIX_SCRIPT)
sbx.commands.run("python3 /home/user/repo/fix.py")

4. Show the diff

DIFF_SCRIPT = """\
with open("/home/user/repo/string_utils.py.orig") as f:
    original_lines = f.readlines()

with open("/home/user/repo/string_utils.py") as f:
    fixed_lines = f.readlines()

for i, (orig, fixed) in enumerate(zip(original_lines, fixed_lines), 1):
    if orig != fixed:
        print(f"Line {i}:")
        print(f"  - {orig.rstrip()}")
        print(f"  + {fixed.rstrip()}")
"""

sbx.files.write("/home/user/repo/diff.py", DIFF_SCRIPT)
diff_result = sbx.commands.run("python3 /home/user/repo/diff.py")
print(diff_result.stdout)

5. Run tests after the fix

    result_after = sbx.commands.run(
        "cd /home/user/repo && python3 -m unittest test_all -v 2>&1"
    )
    print(result_after.stdout)
    print(f"Exit code (after): {result_after.exit_code}")  # 0 (all pass)

    before_status = "PASS" if result_before.exit_code == 0 else "FAIL"
    after_status  = "PASS" if result_after.exit_code == 0 else "FAIL"
    print(f"Before fix: {before_status}")
    print(f"After fix:  {after_status}")
finally:
    sbx.kill()

Expected Output

--- Running Tests (BEFORE fix) ---
test_capitalize_words ... ok
test_count_vowels ... FAIL
test_is_palindrome ... ok
test_reverse_string ... ok
test_factorial ... ok
test_fibonacci ... ok
test_gcd ... ok

FAILED (failures=1)
Exit code: 1

--- Applying Bug Fix ---
  Fix applied: added 'u' to vowels in count_vowels()

--- Diff (before vs after) ---
--- string_utils.py (before)
+++ string_utils.py (after)

Line 10:
  -     return sum(1 for c in s.lower() if c in "aeio")
  +     return sum(1 for c in s.lower() if c in "aeiou")

--- Running Tests (AFTER fix) ---
test_capitalize_words ... ok
test_count_vowels ... ok
test_is_palindrome ... ok
test_reverse_string ... ok
...

Ran 7 tests in 0.001s

OK
Exit code: 0

--- Summary ---
  Before fix: FAIL (exit code 1)
  After fix:  PASS (exit code 0)

Extending to Real Git Repositories

The example simulates a repository by uploading files. To work with a real git repository, install git first and then clone:
sbx = Sandbox.create(
    template="python",
    timeout=600,
    network={"allow_out": ["github.com"]},  # Allow GitHub access
)
sbx.commands.run("apt-get install -y git 2>&1")
sbx.commands.run(
    "git clone https://github.com/your-org/your-repo /home/user/repo 2>&1"
)
When cloning a public repository, set network={"allow_out": ["github.com"]} so the sandbox can reach GitHub but nothing else. For private repositories, pass a personal access token as an environment variable and restrict the allow-list to github.com only.