[Arch] Removing docker exec box (#2802)

* depracting docker exec box

* remove doc exec from workflow and docs
This commit is contained in:
Xingyao Wang
2024-07-05 07:15:25 +08:00
committed by GitHub
parent e6cdf18d3b
commit 0d3b3ffbf8
13 changed files with 92 additions and 554 deletions

View File

@@ -132,7 +132,7 @@ jobs:
fail-fast: false
matrix:
python-version: ["3.11"]
sandbox: ["ssh", "exec", "local"]
sandbox: ["ssh", "local"]
steps:
- uses: actions/checkout@v4

View File

@@ -55,7 +55,7 @@ jobs:
env:
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SANDBOX_TYPE: exec
SANDBOX_TYPE: ssh
run: |
# Append path to launch poetry
export PATH="/github/home/.local/bin:$PATH"

View File

@@ -35,7 +35,7 @@ jobs:
echo "" >> task.txt
echo "BODY:" >> task.txt
echo "${ISSUE_BODY}" >> task.txt
- name: Set up environment
run: |
curl -sSL https://install.python-poetry.org | python3 -
@@ -50,7 +50,7 @@ jobs:
ISSUE_BODY: ${{ github.event.issue.body }}
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SANDBOX_TYPE: exec
SANDBOX_TYPE: ssh
run: |
# Append path to launch poetry
export PATH="/github/home/.local/bin:$PATH"

View File

@@ -3,7 +3,7 @@
CommitWriterAgent can help write git commit message. Example:
```bash
WORKSPACE_MOUNT_PATH="`PWD`" SANDBOX_TYPE="exec" \
WORKSPACE_MOUNT_PATH="`PWD`" SANDBOX_TYPE="ssh" \
poetry run python opendevin/core/main.py -t "dummy task" -c CommitWriterAgent -d ./
```

View File

@@ -66,7 +66,6 @@ en particulier Windows, cela semble échouer.
* Assurez-vous d'avoir les dernières versions de WSL et Docker
* Vérifiez que votre distribution dans WSL est également à jour
* Essayez [ce guide de réinstallation](https://github.com/OpenDevin/OpenDevin/issues/1156#issuecomment-2064549427)
* Définissez `-e SANDBOX_TYPE=exec` pour passer au conteneur ExecBox de Docker
## Impossible de se connecter à LLM

View File

@@ -63,7 +63,6 @@ pexpect.pxssh.ExceptionPxssh: Could not establish connection to host
* 确保拥有最新版本的 WSL 和 Docker
* 检查您的 WSL 分发版也已更新
* 尝试[此重新安装指南](https://github.com/OpenDevin/OpenDevin/issues/1156#issuecomment-2064549427)
* 设置 `-e SANDBOX_TYPE=exec` 切换到 ExecBox Docker 容器
## 无法连接到 LLM

View File

@@ -76,7 +76,6 @@ especially Windows, this seems to fail.
* Be sure to have the latest versions of WSL and Docker
* Check that your distribution in WSL is up to date as well
* Try [this reinstallation guide](https://github.com/OpenDevin/OpenDevin/issues/1156#issuecomment-2064549427)
* Set `-e SANDBOX_TYPE=exec` to switch to the ExecBox docker container
---
### Unable to connect to LLM

View File

@@ -1,7 +1,6 @@
from .docker.exec_box import DockerExecBox
from .docker.local_box import LocalBox
from .docker.ssh_box import DockerSSHBox
from .e2b.sandbox import E2BBox
from .sandbox import Sandbox
__all__ = ['Sandbox', 'DockerSSHBox', 'DockerExecBox', 'E2BBox', 'LocalBox']
__all__ = ['Sandbox', 'DockerSSHBox', 'E2BBox', 'LocalBox']

View File

@@ -1,410 +0,0 @@
import atexit
import os
import shlex
import sys
import tarfile
import time
import uuid
from collections import namedtuple
from glob import glob
import docker
from opendevin.core.config import config
from opendevin.core.const.guide_url import TROUBLESHOOTING_URL
from opendevin.core.exceptions import SandboxInvalidBackgroundCommandError
from opendevin.core.logger import opendevin_logger as logger
from opendevin.core.schema import CancellableStream
from opendevin.runtime.docker.process import DockerProcess, Process
from opendevin.runtime.sandbox import Sandbox
ExecResult = namedtuple('ExecResult', 'exit_code,output')
""" A result of Container.exec_run with the properties ``exit_code`` and
``output``. """
class DockerExecCancellableStream(CancellableStream):
# Reference: https://github.com/docker/docker-py/issues/1989
def __init__(self, _client, _id, _output):
super().__init__(self.read_output())
self._id = _id
self._client = _client
self._output = _output
def close(self):
self.closed = True
def exit_code(self):
return self.inspect()['ExitCode']
def inspect(self):
return self._client.api.exec_inspect(self._id)
def read_output(self):
for chunk in self._output:
yield chunk.decode('utf-8')
def container_exec_run(
container,
cmd,
stdout=True,
stderr=True,
stdin=False,
tty=False,
privileged=False,
user='',
detach=False,
stream=False,
socket=False,
environment=None,
workdir=None,
) -> ExecResult:
exec_id = container.client.api.exec_create(
container.id,
cmd,
stdout=stdout,
stderr=stderr,
stdin=stdin,
tty=tty,
privileged=privileged,
user=user,
environment=environment,
workdir=workdir,
)['Id']
output = container.client.api.exec_start(
exec_id, detach=detach, tty=tty, stream=stream, socket=socket
)
if stream:
return ExecResult(
None, DockerExecCancellableStream(container.client, exec_id, output)
)
if socket:
return ExecResult(None, output)
return ExecResult(container.client.api.exec_inspect(exec_id)['ExitCode'], output)
class DockerExecBox(Sandbox):
instance_id: str
container_image: str
container_name_prefix = 'opendevin-sandbox-'
container_name: str
container: docker.models.containers.Container
docker_client: docker.DockerClient
cur_background_id = 0
background_commands: dict[int, Process] = {}
def __init__(
self,
container_image: str | None = None,
timeout: int = config.sandbox_timeout,
sid: str | None = None,
):
# Initialize docker client. Throws an exception if Docker is not reachable.
try:
self.docker_client = docker.from_env()
except Exception as ex:
logger.exception(
f'Error creating controller. Please check Docker is running and visit `{TROUBLESHOOTING_URL}` for more debugging information.',
exc_info=False,
)
raise ex
self.instance_id = (
sid + str(uuid.uuid4()) if sid is not None else str(uuid.uuid4())
)
# TODO: this timeout is actually essential - need a better way to set it
# if it is too short, the container may still waiting for previous
# command to finish (e.g. apt-get update)
# if it is too long, the user may have to wait for a unnecessary long time
self.timeout = timeout
self.container_image = (
config.sandbox_container_image
if container_image is None
else container_image
)
self.container_name = self.container_name_prefix + self.instance_id
logger.info(
'Starting Docker container with image %s, sandbox workspace dir=%s',
self.container_image,
self.sandbox_workspace_dir,
)
# always restart the container, cuz the initial be regarded as a new session
self.restart_docker_container()
if self.run_as_devin:
self.setup_devin_user()
atexit.register(self.close)
super().__init__()
def setup_devin_user(self):
cmds = [
f'useradd --shell /bin/bash -u {self.user_id} -o -c "" -m devin',
r"echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers",
'sudo adduser devin sudo',
]
for cmd in cmds:
exit_code, logs = self.container.exec_run(
['/bin/bash', '-c', cmd],
workdir=self.sandbox_workspace_dir,
environment=self._env,
)
if exit_code != 0:
raise Exception(f'Failed to setup devin user: {logs}')
def get_exec_cmd(self, cmd: str) -> list[str]:
if self.run_as_devin:
return ['su', 'devin', '-c', cmd]
else:
return ['/bin/bash', '-c', cmd]
def read_logs(self, id) -> str:
if id not in self.background_commands:
raise SandboxInvalidBackgroundCommandError()
bg_cmd = self.background_commands[id]
return bg_cmd.read_logs()
def execute(
self, cmd: str, stream: bool = False, timeout: int | None = None
) -> tuple[int, str | CancellableStream]:
timeout = timeout if timeout is not None else self.timeout
wrapper = f'timeout {self.timeout}s bash -c {shlex.quote(cmd)}'
_exit_code, _output = container_exec_run(
self.container,
wrapper,
stream=stream,
workdir=self.sandbox_workspace_dir,
environment=self._env,
)
if stream:
return _exit_code, _output
print(_output)
_output = _output.decode('utf-8')
if _output.endswith('\n'):
_output = _output[:-1]
return _exit_code, _output
def copy_to(self, host_src: str, sandbox_dest: str, recursive: bool = False):
# mkdir -p sandbox_dest if it doesn't exist
exit_code, logs = self.container.exec_run(
['/bin/bash', '-c', f'mkdir -p {sandbox_dest}'],
workdir=self.sandbox_workspace_dir,
environment=self._env,
)
if exit_code != 0:
raise Exception(
f'Failed to create directory {sandbox_dest} in sandbox: {logs}'
)
if recursive:
assert os.path.isdir(
host_src
), 'Source must be a directory when recursive is True'
files = glob(host_src + '/**/*', recursive=True)
srcname = os.path.basename(host_src)
tar_filename = os.path.join(os.path.dirname(host_src), srcname + '.tar')
with tarfile.open(tar_filename, mode='w') as tar:
for file in files:
tar.add(
file, arcname=os.path.relpath(file, os.path.dirname(host_src))
)
else:
assert os.path.isfile(
host_src
), 'Source must be a file when recursive is False'
srcname = os.path.basename(host_src)
tar_filename = os.path.join(os.path.dirname(host_src), srcname + '.tar')
with tarfile.open(tar_filename, mode='w') as tar:
tar.add(host_src, arcname=srcname)
with open(tar_filename, 'rb') as f:
data = f.read()
self.container.put_archive(os.path.dirname(sandbox_dest), data)
os.remove(tar_filename)
def execute_in_background(self, cmd: str) -> Process:
result = self.container.exec_run(
self.get_exec_cmd(cmd),
socket=True,
workdir=self.sandbox_workspace_dir,
environment=self._env,
)
result.output._sock.setblocking(0)
pid = self.get_pid(cmd)
bg_cmd = DockerProcess(self.cur_background_id, cmd, result, pid)
self.background_commands[bg_cmd.pid] = bg_cmd
self.cur_background_id += 1
return bg_cmd
def get_pid(self, cmd):
exec_result = self.container.exec_run('ps aux', environment=self._env)
processes = exec_result.output.decode('utf-8').splitlines()
cmd = ' '.join(self.get_exec_cmd(cmd))
for process in processes:
if cmd in process:
pid = process.split()[1] # second column is the pid
return pid
return None
def kill_background(self, id: int) -> Process:
if id not in self.background_commands:
raise SandboxInvalidBackgroundCommandError()
bg_cmd = self.background_commands[id]
if bg_cmd.pid is not None:
self.container.exec_run(
f'kill -9 {bg_cmd.pid}',
workdir=self.sandbox_workspace_dir,
environment=self._env,
)
assert isinstance(bg_cmd, DockerProcess)
bg_cmd.result.output.close()
self.background_commands.pop(id)
return bg_cmd
def stop_docker_container(self):
try:
container = self.docker_client.containers.get(self.container_name)
container.stop()
container.remove()
elapsed = 0
while container.status != 'exited':
time.sleep(1)
elapsed += 1
if elapsed > self.timeout:
break
container = self.docker_client.containers.get(self.container_name)
except docker.errors.NotFound:
pass
def is_container_running(self):
try:
container = self.docker_client.containers.get(self.container_name)
if container.status == 'running':
self.container = container
return True
return False
except docker.errors.NotFound:
return False
def restart_docker_container(self):
try:
self.stop_docker_container()
logger.info('Container stopped')
except docker.errors.DockerException as e:
logger.exception('Failed to stop container', exc_info=False)
raise e
try:
# start the container
mount_dir = config.workspace_mount_path
self.container = self.docker_client.containers.run(
self.container_image,
command='tail -f /dev/null',
network_mode='host',
working_dir=self.sandbox_workspace_dir,
name=self.container_name,
detach=True,
volumes={mount_dir: {'bind': self.sandbox_workspace_dir, 'mode': 'rw'}},
)
logger.info('Container started')
except Exception as ex:
logger.exception('Failed to start container', exc_info=False)
raise ex
# wait for container to be ready
elapsed = 0
while self.container.status != 'running':
if self.container.status == 'exited':
logger.info('container exited')
logger.info('container logs:')
logger.info(self.container.logs())
break
time.sleep(1)
elapsed += 1
self.container = self.docker_client.containers.get(self.container_name)
if elapsed > self.timeout:
break
if self.container.status != 'running':
raise Exception('Failed to start container')
# clean up the container, cannot do it in __del__ because the python interpreter is already shutting down
def close(self):
containers = self.docker_client.containers.list(all=True)
for container in containers:
try:
if container.name.startswith(self.container_name_prefix):
container.remove(force=True)
except docker.errors.NotFound:
pass
self.docker_client.close()
def get_working_directory(self):
return self.sandbox_workspace_dir
@property
def user_id(self):
return config.sandbox_user_id
@property
def run_as_devin(self):
# FIXME: On some containers, the devin user doesn't have enough permission, e.g. to install packages
# How do we make this more flexible?
return config.run_as_devin
@property
def sandbox_workspace_dir(self):
return config.workspace_mount_path_in_sandbox
if __name__ == '__main__':
try:
exec_box = DockerExecBox()
except Exception as e:
logger.exception('Failed to start Docker container: %s', e)
sys.exit(1)
logger.info(
"Interactive Docker container started. Type 'exit' or use Ctrl+C to exit."
)
bg_cmd = exec_box.execute_in_background(
"while true; do echo -n '.' && sleep 1; done"
)
sys.stdout.flush()
try:
while True:
try:
user_input = input('>>> ')
except EOFError:
logger.info('Exiting...')
break
if user_input.lower() == 'exit':
logger.info('Exiting...')
break
if user_input.lower() == 'kill':
exec_box.kill_background(bg_cmd.pid)
logger.info('Background process killed')
continue
exit_code, output = exec_box.execute(user_input)
logger.info('exit code: %d', exit_code)
logger.info(output)
if bg_cmd.pid in exec_box.background_commands:
logs = exec_box.read_logs(bg_cmd.pid)
logger.info('background logs: %s', logs)
sys.stdout.flush()
except KeyboardInterrupt:
logger.info('Exiting...')
exec_box.close()

View File

@@ -26,7 +26,6 @@ from opendevin.events.observation import (
)
from opendevin.events.serialization.action import ACTION_TYPE_TO_CLASS
from opendevin.runtime import (
DockerExecBox,
DockerSSHBox,
E2BBox,
LocalBox,
@@ -38,10 +37,8 @@ from opendevin.runtime.tools import RuntimeTool
from opendevin.storage import FileStore, InMemoryFileStore
def create_sandbox(sid: str = 'default', sandbox_type: str = 'exec') -> Sandbox:
if sandbox_type == 'exec':
return DockerExecBox(sid=sid)
elif sandbox_type == 'local':
def create_sandbox(sid: str = 'default', sandbox_type: str = 'ssh') -> Sandbox:
if sandbox_type == 'local':
return LocalBox()
elif sandbox_type == 'ssh':
return DockerSSHBox(sid=sid)

View File

@@ -99,7 +99,7 @@ class AgentSession:
if not self.runtime or not isinstance(self.runtime.sandbox, DockerSSHBox):
logger.warning(
'CodeActAgent requires DockerSSHBox as sandbox! Using other sandbox that are not stateful'
' (LocalBox, DockerExecBox) will not work properly.'
' LocalBox will not work properly.'
)
self.runtime.init_sandbox_plugins(agent.sandbox_plugins)
self.runtime.init_runtime_tools(agent.runtime_tools)

View File

@@ -81,23 +81,18 @@ def test_sandbox_jupyter_plugin_backticks(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox()]:
box.init_plugins([JupyterRequirement])
test_code = "print('Hello, `World`!')"
expected_write_command = (
"cat > /tmp/opendevin_jupyter_temp.py <<'EOL'\n" f'{test_code}\n' 'EOL'
)
expected_execute_command = (
'cat /tmp/opendevin_jupyter_temp.py | execute_cli'
)
exit_code, output = box.execute(expected_write_command)
exit_code, output = box.execute(expected_execute_command)
print(output)
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
assert output.strip() == 'Hello, `World`!', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
box.init_plugins([JupyterRequirement])
test_code = "print('Hello, `World`!')"
expected_write_command = (
"cat > /tmp/opendevin_jupyter_temp.py <<'EOL'\n" f'{test_code}\n' 'EOL'
)
expected_execute_command = 'cat /tmp/opendevin_jupyter_temp.py | execute_cli'
exit_code, output = box.execute(expected_write_command)
exit_code, output = box.execute(expected_execute_command)
print(output)
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
assert output.strip() == 'Hello, `World`!', (
'The output should be the same as the input for ' + box.__class__.__name__
)
box.close()

View File

@@ -6,7 +6,6 @@ from unittest.mock import patch
import pytest
from opendevin.core.config import config
from opendevin.runtime.docker.exec_box import DockerExecBox
from opendevin.runtime.docker.local_box import LocalBox
from opendevin.runtime.docker.ssh_box import DockerSSHBox, split_bash_commands
from opendevin.runtime.plugins import AgentSkillsRequirement, JupyterRequirement
@@ -22,7 +21,7 @@ def temp_dir(monkeypatch):
def test_env_vars(temp_dir):
os.environ['SANDBOX_ENV_FOOBAR'] = 'BAZ'
for box_class in [DockerSSHBox, DockerExecBox, LocalBox]:
for box_class in [DockerSSHBox, LocalBox]:
box = box_class()
box.add_to_env('QUUX', 'abc"def')
assert box._env['FOOBAR'] == 'BAZ'
@@ -137,18 +136,15 @@ def test_ssh_box_multi_line_cmd_run_as_devin(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox(), DockerExecBox()]:
exit_code, output = box.execute('pwd && ls -l')
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
expected_lines = ['/workspace', 'total 0']
line_sep = '\r\n' if isinstance(box, DockerSSHBox) else '\n'
assert output == line_sep.join(expected_lines), (
'The output should be the same as the input for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
exit_code, output = box.execute('pwd && ls -l')
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
expected_lines = ['/workspace', 'total 0']
line_sep = '\r\n' if isinstance(box, DockerSSHBox) else '\n'
assert output == line_sep.join(expected_lines), (
'The output should be the same as the input for ' + box.__class__.__name__
)
box.close()
def test_ssh_box_stateful_cmd_run_as_devin(temp_dir):
@@ -158,29 +154,23 @@ def test_ssh_box_stateful_cmd_run_as_devin(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [
DockerSSHBox()
]: # FIXME: DockerExecBox() does not work with stateful commands
exit_code, output = box.execute('mkdir test')
assert exit_code == 0, 'The exit code should be 0.'
assert output.strip() == ''
box = DockerSSHBox()
exit_code, output = box.execute('mkdir test')
assert exit_code == 0, 'The exit code should be 0.'
assert output.strip() == ''
exit_code, output = box.execute('cd test')
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
assert output.strip() == '', (
'The output should be empty for ' + box.__class__.__name__
)
exit_code, output = box.execute('cd test')
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
assert output.strip() == '', (
'The output should be empty for ' + box.__class__.__name__
)
exit_code, output = box.execute('pwd')
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
assert output.strip() == '/workspace/test', (
'The output should be /workspace for ' + box.__class__.__name__
)
box.close()
exit_code, output = box.execute('pwd')
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
assert output.strip() == '/workspace/test', (
'The output should be /workspace for ' + box.__class__.__name__
)
box.close()
def test_ssh_box_failed_cmd_run_as_devin(temp_dir):
@@ -190,13 +180,13 @@ def test_ssh_box_failed_cmd_run_as_devin(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox(), DockerExecBox()]:
exit_code, output = box.execute('non_existing_command')
assert exit_code != 0, (
'The exit code should not be 0 for a failed command for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
exit_code, output = box.execute('non_existing_command')
assert exit_code != 0, (
'The exit code should not be 0 for a failed command for '
+ box.__class__.__name__
)
box.close()
def test_single_multiline_command(temp_dir):
@@ -205,23 +195,14 @@ def test_single_multiline_command(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox(), DockerExecBox()]:
exit_code, output = box.execute('echo \\\n -e "foo"')
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
if isinstance(box, DockerExecBox):
assert output == 'foo', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
else:
# FIXME: why is there a `>` in the output? Probably PS2?
assert output == '> foo', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
exit_code, output = box.execute('echo \\\n -e "foo"')
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
# FIXME: why is there a `>` in the output? Probably PS2?
assert output == '> foo', (
'The output should be the same as the input for ' + box.__class__.__name__
)
box.close()
def test_multiline_echo(temp_dir):
@@ -230,23 +211,14 @@ def test_multiline_echo(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox(), DockerExecBox()]:
exit_code, output = box.execute('echo -e "hello\nworld"')
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
if isinstance(box, DockerExecBox):
assert output == 'hello\nworld', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
else:
# FIXME: why is there a `>` in the output?
assert output == '> hello\r\nworld', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
exit_code, output = box.execute('echo -e "hello\nworld"')
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
# FIXME: why is there a `>` in the output?
assert output == '> hello\r\nworld', (
'The output should be the same as the input for ' + box.__class__.__name__
)
box.close()
def test_sandbox_whitespace(temp_dir):
@@ -256,22 +228,13 @@ def test_sandbox_whitespace(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox(), DockerExecBox()]:
exit_code, output = box.execute('echo -e "\\n\\n\\n"')
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
if isinstance(box, DockerExecBox):
assert output == '\n\n\n', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
else:
assert output == '\r\n\r\n\r\n', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
exit_code, output = box.execute('echo -e "\\n\\n\\n"')
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
assert output == '\r\n\r\n\r\n', (
'The output should be the same as the input for ' + box.__class__.__name__
)
box.close()
def test_sandbox_jupyter_plugin(temp_dir):
@@ -281,18 +244,15 @@ def test_sandbox_jupyter_plugin(temp_dir):
), patch.object(config, 'run_as_devin', new='true'), patch.object(
config, 'sandbox_type', new='ssh'
):
for box in [DockerSSHBox()]:
box.init_plugins([JupyterRequirement])
exit_code, output = box.execute('echo "print(1)" | execute_cli')
print(output)
assert exit_code == 0, (
'The exit code should be 0 for ' + box.__class__.__name__
)
assert output == '1\r\n', (
'The output should be the same as the input for '
+ box.__class__.__name__
)
box.close()
box = DockerSSHBox()
box.init_plugins([JupyterRequirement])
exit_code, output = box.execute('echo "print(1)" | execute_cli')
print(output)
assert exit_code == 0, 'The exit code should be 0 for ' + box.__class__.__name__
assert output == '1\r\n', (
'The output should be the same as the input for ' + box.__class__.__name__
)
box.close()
def _test_sandbox_jupyter_agentskills_fileop_pwd_impl(box):
@@ -379,8 +339,8 @@ def test_sandbox_jupyter_agentskills_fileop_pwd(temp_dir):
config, 'sandbox_type', new='ssh'
), patch.object(config, 'enable_auto_lint', new=True):
assert config.enable_auto_lint
for box in [DockerSSHBox()]:
_test_sandbox_jupyter_agentskills_fileop_pwd_impl(box)
box = DockerSSHBox()
_test_sandbox_jupyter_agentskills_fileop_pwd_impl(box)
@pytest.mark.skipif(
@@ -398,5 +358,5 @@ def test_agnostic_sandbox_jupyter_agentskills_fileop_pwd(temp_dir):
config, 'sandbox_container_image', new=base_sandbox_image
), patch.object(config, 'enable_auto_lint', new=False):
assert not config.enable_auto_lint
for box in [DockerSSHBox()]:
_test_sandbox_jupyter_agentskills_fileop_pwd_impl(box)
box = DockerSSHBox()
_test_sandbox_jupyter_agentskills_fileop_pwd_impl(box)