Skip to content

Commit cbb173a

Browse files
committed
🛀 docs + isort + mypy + flake8
1 parent d5c0a1b commit cbb173a

File tree

16 files changed

+389
-206
lines changed

16 files changed

+389
-206
lines changed

codeboxapi/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
"""
2+
CodeBox is the simplest cloud infrastructure for your LLM Apps and Services.
3+
4+
The `codeboxapi` package provides a Python API Wrapper for the Codebox API.
5+
The package includes modules for configuring the client, setting the API key,
6+
and interacting with Codebox instances.
7+
"""
8+
from codeboxapi.box.codebox import CodeBox
19
from codeboxapi.config import settings
210
from codeboxapi.utils import set_api_key
3-
from codeboxapi.box.codebox import CodeBox
4-
511

612
__all__ = [
713
"CodeBox",

codeboxapi/box/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
"""
2+
This module contains the box classes that are used to run code in a sandboxed
3+
environment. The `BaseBox` class is the base class for all box classes.
4+
The `LocalBox` class is used to run code in a local testing environment. The
5+
`CodeBox` class is used to run code in a remote sandboxed environment.
6+
"""
7+
18
from .basebox import BaseBox
2-
from .localbox import LocalBox
39
from .codebox import CodeBox
10+
from .localbox import LocalBox
11+
12+
__all__ = [
13+
"BaseBox",
14+
"CodeBox",
15+
"LocalBox",
16+
]

codeboxapi/box/basebox.py

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,103 @@
1-
import os
2-
from typing import Optional
1+
""" Abstract Base Class for Isolated Execution Environments (CodeBox's) """
2+
3+
from abc import ABC, abstractmethod
34
from datetime import datetime
5+
from os import PathLike
6+
from typing import Optional
7+
from uuid import UUID
8+
49
from typing_extensions import Self
5-
from abc import ABC, abstractmethod
6-
from codeboxapi.schema import (
7-
CodeBoxStatus,
8-
CodeBoxOutput,
9-
CodeBoxFile,
10-
)
10+
11+
from codeboxapi.schema import CodeBoxFile, CodeBoxOutput, CodeBoxStatus
1112

1213

1314
class BaseBox(ABC):
14-
"""
15-
ABC for Isolated Execution Environments
16-
"""
15+
"""CodeBox Abstract Base Class"""
1716

18-
def __init__(self) -> None:
19-
self.id: Optional[int] = None
17+
def __init__(self, session_id: Optional[UUID] = None) -> None:
18+
"""Initialize the CodeBox instance"""
19+
self.session_id = session_id
2020
self.last_interaction = datetime.now()
2121

2222
def _update(self) -> None:
23+
"""Update last interaction time"""
2324
self.last_interaction = datetime.now()
2425

2526
@abstractmethod
2627
def start(self) -> CodeBoxStatus:
27-
...
28+
"""Startup the CodeBox instance"""
2829

2930
@abstractmethod
3031
async def astart(self) -> CodeBoxStatus:
31-
...
32+
"""Async Startup the CodeBox instance"""
3233

3334
@abstractmethod
3435
def status(self) -> CodeBoxStatus:
35-
...
36+
"""Get the current status of the CodeBox instance"""
3637

3738
@abstractmethod
3839
async def astatus(self) -> CodeBoxStatus:
39-
...
40+
"""Async Get the current status of the CodeBox instance"""
4041

4142
@abstractmethod
4243
def run(
43-
self, code: Optional[str] = None, file_path: Optional[os.PathLike] = None
44+
self, code: Optional[str] = None, file_path: Optional[PathLike] = None
4445
) -> CodeBoxOutput:
45-
...
46+
"""Execute python code inside the CodeBox instance"""
4647

4748
@abstractmethod
4849
async def arun(
49-
self, code: Optional[str] = None, file_path: Optional[os.PathLike] = None
50+
self, code: str, file_path: Optional[PathLike] = None
5051
) -> CodeBoxOutput:
51-
...
52+
"""Async Execute python code inside the CodeBox instance"""
5253

5354
@abstractmethod
5455
def upload(self, file_name: str, content: bytes) -> CodeBoxStatus:
55-
...
56+
"""Upload a file as bytes to the CodeBox instance"""
5657

5758
@abstractmethod
5859
async def aupload(self, file_name: str, content: bytes) -> CodeBoxStatus:
59-
...
60+
"""Async Upload a file as bytes to the CodeBox instance"""
6061

6162
@abstractmethod
6263
def download(self, file_name: str) -> CodeBoxFile:
63-
...
64+
"""Download a file as CodeBoxFile schema"""
6465

6566
@abstractmethod
6667
async def adownload(self, file_name: str) -> CodeBoxFile:
67-
...
68+
"""Async Download a file as CodeBoxFile schema"""
6869

6970
@abstractmethod
7071
def install(self, package_name: str) -> CodeBoxStatus:
71-
...
72+
"""Install a python package to the venv"""
7273

7374
@abstractmethod
7475
async def ainstall(self, package_name: str) -> CodeBoxStatus:
75-
...
76+
"""Async Install a python package to the venv"""
7677

7778
@abstractmethod
7879
def list_files(self) -> list[CodeBoxFile]:
79-
...
80+
"""List all available files inside the CodeBox instance"""
8081

8182
@abstractmethod
8283
async def alist_files(self) -> list[CodeBoxFile]:
83-
...
84+
"""Async List all available files inside the CodeBox instance"""
8485

85-
# @abstractmethod # TODO: implement
86-
# def restart(self) -> CodeBoxStatus: ...
86+
@abstractmethod
87+
def restart(self) -> CodeBoxStatus:
88+
"""Restart the jupyter kernel inside the CodeBox instance"""
8789

88-
# @abstractmethod # TODO: implement
89-
# async def arestart(self) -> CodeBoxStatus: ...
90+
@abstractmethod
91+
async def arestart(self) -> CodeBoxStatus:
92+
"""Async Restart the jupyter kernel inside the CodeBox instance"""
9093

9194
@abstractmethod
9295
def stop(self) -> CodeBoxStatus:
93-
...
96+
"""Terminate the CodeBox instance"""
9497

9598
@abstractmethod
9699
async def astop(self) -> CodeBoxStatus:
97-
...
100+
"""Async Terminate the CodeBox instance"""
98101

99102
def __enter__(self) -> Self:
100103
self.start()
@@ -109,3 +112,9 @@ def __exit__(self, exc_type, exc_value, traceback) -> None:
109112

110113
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
111114
await self.astop()
115+
116+
def __repr__(self) -> str:
117+
return f"<{self.__class__.__name__} id={self.session_id}>"
118+
119+
def __str__(self) -> str:
120+
return self.__repr__()

codeboxapi/box/codebox.py

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,49 @@
1-
import os
1+
"""
2+
CodeBox API Wrapper
3+
~~~~~~~~~~~~~~~~~~~
4+
5+
A basic wrapper for the CodeBox API.
6+
7+
Usage
8+
-----
9+
10+
.. code-block:: python
11+
12+
from codeboxapi import CodeBox
13+
14+
with CodeBox() as codebox:
15+
codebox.status()
16+
codebox.run(code="print('Hello World!')")
17+
codebox.install("python-package")
18+
codebox.upload("test.txt", b"Hello World!")
19+
codebox.list_files()
20+
codebox.download("test.txt")
21+
22+
.. code-block:: python
23+
24+
from codeboxapi import CodeBox
25+
26+
async with CodeBox() as codebox:
27+
await codebox.astatus()
28+
await codebox.arun(code="print('Hello World!')")
29+
await codebox.ainstall("python-package")
30+
await codebox.aupload("test.txt", b"Hello World!")
31+
await codebox.alist_files()
32+
await codebox.adownload("test.txt")
33+
34+
"""
35+
36+
from os import PathLike
237
from typing import Any, Optional
3-
from codeboxapi import settings
4-
from codeboxapi.box import BaseBox
5-
from codeboxapi.utils import base_request, abase_request
6-
from codeboxapi.schema import CodeBoxStatus, CodeBoxOutput, CodeBoxFile
38+
from uuid import UUID
39+
740
from aiohttp import ClientSession
841

42+
from codeboxapi.box.basebox import BaseBox
43+
from codeboxapi.config import settings
44+
from codeboxapi.schema import CodeBoxFile, CodeBoxOutput, CodeBoxStatus
45+
from codeboxapi.utils import abase_request, base_request
46+
947

1048
class CodeBox(BaseBox):
1149
"""
@@ -17,39 +55,47 @@ def __new__(cls, *args, **kwargs):
1755
from .localbox import LocalBox
1856

1957
return LocalBox(*args, **kwargs)
20-
else:
21-
return super().__new__(cls, *args, **kwargs)
58+
return super().__new__(cls, *args, **kwargs)
2259

2360
def __init__(self, *args, **kwargs) -> None:
2461
super().__init__(*args, **kwargs)
25-
self.session: Optional[ClientSession] = None
62+
self.session_id: Optional[UUID] = kwargs.get("id", None)
63+
self.aiohttp_session: Optional[ClientSession] = None
2664

2765
def codebox_request(self, method, endpoint, *args, **kwargs) -> dict[str, Any]:
66+
"""Basic request to the CodeBox API"""
2867
self._update()
29-
return base_request(method, f"/codebox/{self.id}" + endpoint, *args, **kwargs)
68+
return base_request(
69+
method, f"/codebox/{self.session_id}" + endpoint, *args, **kwargs
70+
)
3071

3172
async def acodebox_request(
3273
self, method, endpoint, *args, **kwargs
3374
) -> dict[str, Any]:
75+
"""Basic async request to the CodeBox API"""
3476
self._update()
35-
if self.session is None:
77+
if self.aiohttp_session is None:
3678
raise RuntimeError("CodeBox session not started")
3779
return await abase_request(
38-
self.session, method, f"/codebox/{self.id}" + endpoint, *args, **kwargs
80+
self.aiohttp_session,
81+
method,
82+
f"/codebox/{self.session_id}" + endpoint,
83+
*args,
84+
**kwargs,
3985
)
4086

4187
def start(self) -> CodeBoxStatus:
42-
self.id = base_request(
88+
self.session_id = base_request(
4389
method="GET",
4490
endpoint="/codebox/start",
4591
)["id"]
4692
return CodeBoxStatus(status="started")
4793

4894
async def astart(self) -> CodeBoxStatus:
49-
self.session = ClientSession()
50-
self.id = (
95+
self.aiohttp_session = ClientSession()
96+
self.session_id = (
5197
await abase_request(
52-
self.session,
98+
self.aiohttp_session,
5399
method="GET",
54100
endpoint="/codebox/start",
55101
)
@@ -72,34 +118,30 @@ async def astatus(self):
72118
)
73119
)
74120

75-
def run(self, code: Optional[str] = None, file_path: Optional[os.PathLike] = None):
76-
if not code and not file_path:
121+
def run(
122+
self, code: Optional[str] = None, file_path: Optional[PathLike] = None
123+
) -> CodeBoxOutput:
124+
if not code and not file_path: # R0801
77125
raise ValueError("Code or file_path must be specified!")
78126

79127
if code and file_path:
80128
raise ValueError("Can only specify code or the file to read_from!")
81129

82130
if file_path:
83-
with open(file_path, "r") as f:
131+
with open(file_path, "r", encoding="utf-8") as f:
84132
code = f.read()
85133

86134
return CodeBoxOutput(
87135
**self.codebox_request(
88136
method="POST",
89-
endpoint=f"/run",
137+
endpoint="/run",
90138
body={"code": code},
91139
)
92140
)
93141

94142
async def arun(
95-
self, code: Optional[str] = None, file_path: Optional[os.PathLike] = None
96-
):
97-
if not code and not file_path:
98-
raise ValueError("Code or file_path must be specified!")
99-
100-
if code and file_path:
101-
raise ValueError("Can only specify code or the file to read_from!")
102-
143+
self, code: str, file_path: Optional[PathLike] = None
144+
) -> CodeBoxOutput:
103145
if file_path: # TODO: Implement this
104146
raise NotImplementedError(
105147
"Reading from FilePath is not supported in async mode yet!"
@@ -108,7 +150,7 @@ async def arun(
108150
return CodeBoxOutput(
109151
**await self.acodebox_request(
110152
method="POST",
111-
endpoint=f"/run",
153+
endpoint="/run",
112154
body={"code": code},
113155
)
114156
)
@@ -193,6 +235,22 @@ async def alist_files(self) -> list[CodeBoxFile]:
193235
)["files"]
194236
]
195237

238+
def restart(self) -> CodeBoxStatus:
239+
return CodeBoxStatus(
240+
**self.codebox_request(
241+
method="POST",
242+
endpoint="/restart",
243+
)
244+
)
245+
246+
async def arestart(self) -> CodeBoxStatus:
247+
return CodeBoxStatus(
248+
**await self.acodebox_request(
249+
method="POST",
250+
endpoint="/restart",
251+
)
252+
)
253+
196254
def stop(self) -> CodeBoxStatus:
197255
return CodeBoxStatus(
198256
**self.codebox_request(
@@ -208,7 +266,7 @@ async def astop(self) -> CodeBoxStatus:
208266
endpoint="/stop",
209267
)
210268
)
211-
if self.session:
212-
await self.session.close()
213-
self.session = None
269+
if self.aiohttp_session:
270+
await self.aiohttp_session.close()
271+
self.aiohttp_session = None
214272
return status

0 commit comments

Comments
 (0)