Skip to content

Commit 46d3557

Browse files
Add Python REPL running in docker container API and docs (#3)
* Add python REPL running inside docker container * communication via HTTP * Update README.md * Add python_runner.py to MANIFEST.in * Update documentation * Try to run tests with single worker * Disable Python REPL tests on CI for now there is timeout and it needs debugging. Locally they work fine.
1 parent 79ab31c commit 46d3557

14 files changed

+553
-34
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
run: pip install -r requirements-dev.txt
6464

6565
- name: Run Tests
66-
run: pytest -v -p no:warnings --junitxml=report.xml tests/
66+
run: CI=1 pytest -v -p no:warnings --junitxml=report.xml tests/
6767

6868
- name: Publish Test Report
6969
uses: actions/upload-artifact@v2

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ repos:
4444
args: []
4545
# You can add additional plugins for mypy below
4646
# such as types-python-dateutil
47-
additional_dependencies: []
47+
additional_dependencies: ["types-requests"]
4848
exclude: (/test_|setup.py|/tests/|docs/)
4949

5050
# Sort imports alphabetically, and automatically separated into sections and by type.

DESCRIPTION.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
`deepsense.ai <https://deepsense.ai>`_ **ds_pycontain**
22
-------------------------------------------------------------
33

4-
**ds_pycontain** is a small python package to help with docker containers and images.
4+
**ds_pycontain** is a small python package to help with docker containers and images and provide Python REPL running in a docker container.
55

66
Example use case you might consider is to isolate python code execution generated by untrusted LLM by running it in a docker container.
77

@@ -11,3 +11,4 @@ This package makes it a bit easier to:
1111
* Pull docker images from dockerhub (or similar).
1212
* Run docker container to execute a one-off command.
1313
* Run docker container to execute a long-running process and communicate with it.
14+
* Run python commands in a container and get the result.

MANIFEST.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
recursive-include src/ py.typed *.pyi VERSION
1+
recursive-include src/ py.typed *.pyi VERSION python_runner.py
22
global-exclude __pycache__
33
global-exclude *.py[cod]

README.md

+39-1
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55

66
[Documentation](https://deepsense-ai.github.io/ds-pycontain/)
77

8-
It is a simple wrapper library around docker python API to make it easier to use in. In particular it was created for langchain isolated repl.
8+
It is a simple wrapper library around docker python API to make it easier to use and to provide Python REPL running in a container.
9+
In particular it was created for langchain isolated python REPL, so agents can run code in isolation.
10+
11+
**Warning**: This package requires docker to be installed and running on the host machine. It also needs more work to make it secure.
912

1013
This package makes it a bit easier to:
1114

1215
* Build docker images from Dockerfiles or in-memory string.
1316
* Pull docker images from dockerhub (or similar).
1417
* Run docker container to execute a one-off command.
1518
* Run docker container to execute a long-running process and communicate with it.
19+
* Run python commands in a container and get the result.
1620

1721
Project boostraped with ds-template: [https://deepsense-ai.github.io/ds-template/](https://deepsense-ai.github.io/ds-template/).
1822

@@ -37,6 +41,8 @@ Project boostraped with ds-template: [https://deepsense-ai.github.io/ds-template
3741

3842
## Docker images
3943
```python
44+
from ds_pycontain import DockerImage
45+
4046
# pull or use alpine:latest
4147
image = DockerImage.from_tag("alpine")
4248
# use provided tag to pull/use the image
@@ -47,6 +53,38 @@ image = DockerImage.from_dockerfile("example/Dockerfile")
4753
image = DockerImage.from_dockerfile("path/to/dir_with_Dockerfile/", name="cow")
4854
```
4955

56+
## Python REPL running in docker container
57+
```python
58+
from ds_pycontain.python_dockerized_repl import PythonContainerREPL
59+
60+
# To start python REPL in container it is easy,
61+
# just be aware that it will take some time to start the container
62+
# and ports might be allocated by OS, so use different port/retry
63+
# if you get error.
64+
repl = PythonContainerREPL(port=7121)
65+
66+
# You can run python commands in the container
67+
# and it will keep state between commands.
68+
out1 = repl.exec("x = [1, 2, 3]")
69+
assert out1 == ""
70+
# Eval returns string representation of the python command
71+
# as it would be in python REPL:
72+
out2 = repl.eval("len(x)")
73+
assert out2 == "3"
74+
75+
# Exec returns captured standard output (stdout)
76+
# so it won't return anything in this case:
77+
out3 = repl.exec("len(x)")
78+
assert out3 == ""
79+
# but exec with print works:
80+
out4 = repl.exec("print(len(x))")
81+
assert out4 == "3\n"
82+
83+
# You can also get error messages if code is wrong:
84+
err = repl.exec("print(x")
85+
assert "SyntaxError" in err
86+
```
87+
5088
# Setup developer environment
5189

5290
To start, you need to setup your local machine.

docs/api/ds_pycontain.rst

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ ds_pycontain.docker_containers module
1212
:undoc-members:
1313
:show-inheritance:
1414

15+
ds_pycontain.python_dockerized_repl module
16+
----------------------------------------------------
17+
18+
.. automodule:: ds_pycontain.python_dockerized_repl
19+
:members:
20+
:undoc-members:
21+
:show-inheritance:
22+
1523
Module contents
1624
---------------
1725

docs/code_documentation.md

+1-24
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,6 @@
11
# Code documentation
22

3-
**ds_pycontain** is a python package which provides an abstraction over the docker API.
4-
5-
Supported functionality covers:
6-
- Building docker images from Dockerfiles
7-
- Pulling docker images from dockerhub (or similar)
8-
- Running docker containers to execute a one-off command
9-
- Running docker containers to execute a long-running process and communicate with it
10-
11-
12-
```python
13-
from ds_pycontain import DockerContainer, DockerImage, get_docker_client
14-
15-
client = get_docker_client()
16-
17-
# This will fetch the image from dockerhub if it is not already present
18-
# with the "latest" tag. Then container is started and commands are run
19-
with DockerContainer(DockerImage.from_tag("alpine")) as container:
20-
ret_code, output = container.run("touch /animal.txt")
21-
assert ret_code == 0
22-
23-
ret_code, output = container.run("ls /")
24-
assert ret_code == 0
25-
assert cast(bytes, output).find(b"animal.txt") >= 0
26-
```
3+
This is the documentation for the code of the project API.
274

285
```{toctree}
296
---

docs/index.rst

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
`deepsense.ai <https://deepsense.ai>`_ - pycontain
66
====================================================================================================================
77

8-
Documentation for **ds_pycontain** python package to work with docker containers and images.
9-
10-
Example use case you might consider is to isolate python code execution generated by untrusted LLM by running it in a docker container.
8+
Documentation for **ds_pycontain** python package to work with docker containers and images, as well as providing python REPL running in a container.
119

1210
This package makes it a bit easier to:
1311

1412
* Build docker images from Dockerfiles or in-memory string.
1513
* Pull docker images from dockerhub (or similar).
1614
* Run docker container to execute a one-off command.
1715
* Run docker container to execute a long-running process and communicate with it.
16+
* Run python code in a docker container and communicate with it.
1817

1918

2019
.. toctree::

docs/licenses.rst

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ Licenses
22
=====================
33

44
List of automatically detected licenses of all detected python packages with `pip-licenses`.
5+
They are all projects that are used in the project development.
56

67
.. include:: licenses_table.rst

docs/project_overview.md

+89-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,94 @@
11
# Project overview
22

3+
**ds_pycontain** is a python package which provides an abstraction over the docker API and provide Python REPL running in a docker container.
4+
5+
Supported functionality covers:
6+
- Building docker images from Dockerfiles
7+
- Pulling docker images from dockerhub (or similar)
8+
- Running docker containers to execute a one-off command
9+
- Running docker containers to execute a long-running process and communicate with it
10+
- Run python commands in a container and get the result.
11+
12+
## Motivation
13+
14+
Main motivation is to allow to orchestrate running unsafe code or commands in isolated environment.
315
The docker API is quite complicated and not well documented or typed.
416
This project aims to provide a higher level abstraction over the docker API.
517

6-
Main motivation is to allow to orchestrate running unsafe code or commands in isolated environment.
18+
What is also provided is **a python REPL running in a docker container**.
19+
20+
This might be useful to improve security for execution of LLM models/agents generated code, which generally should not be trusted.
21+
22+
## Example code snippets
23+
24+
### Execute commands in container running in the background:
25+
26+
Below is a short snippet showcasing how to run docker container in the background and execute commands in it.
27+
28+
```python
29+
from ds_pycontain import DockerContainer, DockerImage, get_docker_client
30+
31+
client = get_docker_client()
32+
33+
# This will fetch the image from dockerhub if it is not already present
34+
# with the "latest" tag. Then container is started and commands are run
35+
with DockerContainer(DockerImage.from_tag("alpine")) as container:
36+
ret_code, output = container.run("touch /animal.txt")
37+
assert ret_code == 0
38+
39+
ret_code, output = container.run("ls /")
40+
assert ret_code == 0
41+
assert cast(bytes, output).find(b"animal.txt") >= 0
42+
```
43+
44+
### Docker images
45+
46+
Images can be pulled from dockerhub or built from dockerfile.
47+
48+
```python
49+
from ds_pycontain import DockerImage
50+
51+
# pull or use alpine:latest
52+
image = DockerImage.from_tag("alpine")
53+
# use provided tag to pull/use the image
54+
image = DockerImage.from_tag("python", tag="3.9-slim")
55+
# use this dockerfile to build a new local image
56+
image = DockerImage.from_dockerfile("example/Dockerfile")
57+
# you can provide a directory path which contains Dockerfile, set custom image name
58+
image = DockerImage.from_dockerfile("path/to/dir_with_Dockerfile/", name="cow")
59+
```
60+
61+
### Python REPL running in docker container
62+
63+
Running Python code in docker container is rather easy with this package.
64+
65+
```python
66+
from ds_pycontain.python_dockerized_repl import PythonContainerREPL
67+
68+
# To start python REPL in container it is easy,
69+
# just be aware that it will take some time to start the container
70+
# and ports might be allocated by OS, so use different port/retry
71+
# if you get error.
72+
repl = PythonContainerREPL(port=7121)
73+
74+
# You can run python commands in the container
75+
# and it will keep state between commands.
76+
out1 = repl.exec("x = [1, 2, 3]")
77+
assert out1 == ""
78+
# Eval returns string representation of the python command
79+
# as it would be in python REPL:
80+
out2 = repl.eval("len(x)")
81+
assert out2 == "3"
82+
83+
# Exec returns captured standard output (stdout)
84+
# so it won't return anything in this case:
85+
out3 = repl.exec("len(x)")
86+
assert out3 == ""
87+
# but exec with print works:
88+
out4 = repl.exec("print(len(x))")
89+
assert out4 == "3\n"
90+
91+
# You can also get error messages if code is wrong:
92+
err = repl.exec("print(x")
93+
assert "SyntaxError" in err
94+
```

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ max-locals=20
119119
min-similarity-lines=10
120120

121121
[tool.bandit]
122-
exclude_dirs = ["venv",]
122+
exclude_dirs = ["venv", "src/ds_pycontain/data"]
123123
# B101 disables errors for asserts in the code
124124
# remember to not use asserts for security and control flows
125125
skips = ["B101"]

0 commit comments

Comments
 (0)