Skip to content

Commit ea4507f

Browse files
committed
docs: add sandbox testing notebook
1 parent 6094a16 commit ea4507f

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed

notebooks/sandbox_testing.ipynb

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# Sandbox Abstraction - Testing Notebook\n",
8+
"\n",
9+
"Interactive notebook for testing the Sandbox abstraction.\n",
10+
"\n",
11+
"## Setup\n",
12+
"```bash\n",
13+
"git clone https://github.com/mkmeral/sdk-python.git\n",
14+
"cd sdk-python && git checkout feat/sandbox-abstraction\n",
15+
"pip install -e '.[dev]'\n",
16+
"```"
17+
]
18+
},
19+
{
20+
"cell_type": "code",
21+
"execution_count": null,
22+
"metadata": {},
23+
"outputs": [],
24+
"source": [
25+
"import asyncio\n",
26+
"from strands.sandbox import LocalSandbox, DockerSandbox, ExecutionResult, Sandbox, ShellBasedSandbox\n",
27+
"print('Imports OK')"
28+
]
29+
},
30+
{
31+
"cell_type": "markdown",
32+
"metadata": {},
33+
"source": [
34+
"## 1. LocalSandbox Basics"
35+
]
36+
},
37+
{
38+
"cell_type": "code",
39+
"execution_count": null,
40+
"metadata": {},
41+
"outputs": [],
42+
"source": [
43+
"async def test_local_basics():\n",
44+
" sandbox = LocalSandbox(working_dir='/tmp/sandbox-test')\n",
45+
" \n",
46+
" # Execute a command\n",
47+
" print('=== Execute ===')\n",
48+
" async for chunk in sandbox.execute('echo Hello && ls -la /tmp/sandbox-test'):\n",
49+
" if isinstance(chunk, str):\n",
50+
" print(chunk, end='')\n",
51+
" else:\n",
52+
" print(f'exit_code={chunk.exit_code}')\n",
53+
" \n",
54+
" # File operations\n",
55+
" print('\\n=== File Ops ===')\n",
56+
" await sandbox.write_file('hello.py', 'print(42)')\n",
57+
" content = await sandbox.read_file('hello.py')\n",
58+
" print(f'Content: {content}')\n",
59+
" print(f'Files: {await sandbox.list_files(\".\")}')\n",
60+
" \n",
61+
" # Execute code\n",
62+
" print('\\n=== Execute Code ===')\n",
63+
" async for chunk in sandbox.execute_code('import sys; print(sys.version)'):\n",
64+
" if isinstance(chunk, str):\n",
65+
" print(chunk, end='')\n",
66+
" else:\n",
67+
" print(f'exit_code={chunk.exit_code}')\n",
68+
"\n",
69+
"await test_local_basics()"
70+
]
71+
},
72+
{
73+
"cell_type": "markdown",
74+
"metadata": {},
75+
"source": [
76+
"## 2. Agent + Sandbox Integration"
77+
]
78+
},
79+
{
80+
"cell_type": "code",
81+
"execution_count": null,
82+
"metadata": {},
83+
"outputs": [],
84+
"source": [
85+
"from strands import Agent\n",
86+
"\n",
87+
"agent1 = Agent()\n",
88+
"print(f'Default: {type(agent1.sandbox).__name__}, dir={agent1.sandbox.working_dir}')\n",
89+
"\n",
90+
"agent2 = Agent(sandbox=LocalSandbox(working_dir='/tmp/custom'))\n",
91+
"print(f'Custom: {type(agent2.sandbox).__name__}, dir={agent2.sandbox.working_dir}')\n",
92+
"\n",
93+
"shared = LocalSandbox(working_dir='/tmp/shared')\n",
94+
"a = Agent(sandbox=shared)\n",
95+
"b = Agent(sandbox=shared)\n",
96+
"print(f'Shared same instance: {a.sandbox is b.sandbox}')"
97+
]
98+
},
99+
{
100+
"cell_type": "markdown",
101+
"metadata": {},
102+
"source": [
103+
"## 3. Concurrent Execution"
104+
]
105+
},
106+
{
107+
"cell_type": "code",
108+
"execution_count": null,
109+
"metadata": {},
110+
"outputs": [],
111+
"source": [
112+
"import time\n",
113+
"\n",
114+
"async def test_concurrent():\n",
115+
" sandbox = LocalSandbox(working_dir='/tmp/concurrent-test')\n",
116+
" \n",
117+
" async def run(name, delay):\n",
118+
" r = await sandbox._execute_to_result(f'sleep {delay} && echo {name}')\n",
119+
" return f'{name}: {r.stdout.strip()}'\n",
120+
" \n",
121+
" start = time.time()\n",
122+
" results = await asyncio.gather(\n",
123+
" run('task1', 0.5), run('task2', 0.3), run('task3', 0.1),\n",
124+
" run('task4', 0.2), run('task5', 0.4),\n",
125+
" )\n",
126+
" elapsed = time.time() - start\n",
127+
" \n",
128+
" for r in results:\n",
129+
" print(r)\n",
130+
" print(f'\\nTotal: {elapsed:.2f}s (should be ~0.5s, not 1.5s)')\n",
131+
"\n",
132+
"await test_concurrent()"
133+
]
134+
},
135+
{
136+
"cell_type": "markdown",
137+
"metadata": {},
138+
"source": [
139+
"## 4. Custom Sandbox (ShellBasedSandbox)"
140+
]
141+
},
142+
{
143+
"cell_type": "code",
144+
"execution_count": null,
145+
"metadata": {},
146+
"outputs": [],
147+
"source": [
148+
"class LoggingSandbox(ShellBasedSandbox):\n",
149+
" def __init__(self, working_dir='/tmp/logging-sandbox'):\n",
150+
" super().__init__()\n",
151+
" self.working_dir = working_dir\n",
152+
" self.log = []\n",
153+
" self._local = LocalSandbox(working_dir=working_dir)\n",
154+
" \n",
155+
" async def start(self):\n",
156+
" await self._local.start()\n",
157+
" self._started = True\n",
158+
" \n",
159+
" async def stop(self):\n",
160+
" await self._local.stop()\n",
161+
" self._started = False\n",
162+
" \n",
163+
" async def execute(self, command, timeout=None):\n",
164+
" self.log.append(command)\n",
165+
" print(f'LOG: {command[:60]}')\n",
166+
" async for chunk in self._local.execute(command, timeout=timeout):\n",
167+
" yield chunk\n",
168+
"\n",
169+
"async def test_custom():\n",
170+
" s = LoggingSandbox()\n",
171+
" await s.write_file('test.txt', 'hello')\n",
172+
" print(f'Content: {await s.read_file(\"test.txt\")}')\n",
173+
" print(f'Files: {await s.list_files(\".\")}')\n",
174+
" r = await s._execute_code_to_result('print(2+2)')\n",
175+
" print(f'Code: {r.stdout.strip()}')\n",
176+
" print(f'Commands logged: {len(s.log)}')\n",
177+
"\n",
178+
"await test_custom()"
179+
]
180+
},
181+
{
182+
"cell_type": "markdown",
183+
"metadata": {},
184+
"source": [
185+
"## 5. Context Manager and Lifecycle"
186+
]
187+
},
188+
{
189+
"cell_type": "code",
190+
"execution_count": null,
191+
"metadata": {},
192+
"outputs": [],
193+
"source": [
194+
"async def test_lifecycle():\n",
195+
" s = LocalSandbox(working_dir='/tmp/lifecycle-test')\n",
196+
" print(f'Before: started={s._started}')\n",
197+
" \n",
198+
" r = await s._execute_to_result('echo auto')\n",
199+
" print(f'After auto-start: started={s._started}, out={r.stdout.strip()}')\n",
200+
" \n",
201+
" await s.stop()\n",
202+
" print(f'After stop: started={s._started}')\n",
203+
" \n",
204+
" async with LocalSandbox(working_dir='/tmp/ctx') as ctx:\n",
205+
" print(f'In ctx: started={ctx._started}')\n",
206+
" r = await ctx._execute_to_result('echo context')\n",
207+
" print(f'Out: {r.stdout.strip()}')\n",
208+
" print(f'After ctx: started={ctx._started}')\n",
209+
"\n",
210+
"await test_lifecycle()"
211+
]
212+
},
213+
{
214+
"cell_type": "markdown",
215+
"metadata": {},
216+
"source": [
217+
"## 6. DockerSandbox (requires Docker)"
218+
]
219+
},
220+
{
221+
"cell_type": "code",
222+
"execution_count": null,
223+
"metadata": {},
224+
"outputs": [],
225+
"source": [
226+
"import shutil\n",
227+
"if shutil.which('docker'):\n",
228+
" async def test_docker():\n",
229+
" async with DockerSandbox(image='python:3.12-slim') as s:\n",
230+
" print(f'Container: {s._container_id}')\n",
231+
" r = await s._execute_to_result('python --version')\n",
232+
" print(f'Python: {r.stdout.strip()}')\n",
233+
" await s.write_file('test.py', 'print(42)')\n",
234+
" print(f'File: {await s.read_file(\"test.py\")}')\n",
235+
" async for c in s.execute_code('import os; print(os.getpid())'):\n",
236+
" if isinstance(c, str): print(c, end='')\n",
237+
" else: print(f'exit={c.exit_code}')\n",
238+
" await test_docker()\n",
239+
"else:\n",
240+
" print('Docker not available')"
241+
]
242+
}
243+
],
244+
"metadata": {
245+
"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
246+
"language_info": {"name": "python", "version": "3.12.0"}
247+
},
248+
"nbformat": 4,
249+
"nbformat_minor": 4
250+
}

0 commit comments

Comments
 (0)