e2b-code-interpreter 0.0.1a1__tar.gz → 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: e2b-code-interpreter
3
- Version: 0.0.1a1
3
+ Version: 0.0.2
4
4
  Summary: E2B Code Interpreter - Stateful code execution
5
5
  Home-page: https://e2b.dev/
6
6
  License: Apache-2.0
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.8
13
13
  Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
16
17
  Requires-Dist: e2b (>=0.14.11)
17
18
  Requires-Dist: pydantic (>1,<3)
18
19
  Requires-Dist: websocket-client (>=1.7.0,<2.0.0)
@@ -43,10 +44,10 @@ pip install e2b-code-interpreter
43
44
  from e2b_code_interpreter import CodeInterpreter
44
45
 
45
46
  with CodeInterpreter() as sandbox:
46
- sandbox.exec_cell("x = 1")
47
+ sandbox.notebook.exec_cell("x = 1")
47
48
 
48
- result = sandbox.exec_cell("x += 1; x")
49
- print(result.text) # outputs 2
49
+ execution = sandbox.notebook.exec_cell("x+=1; x")
50
+ print(execution.text) # outputs 2
50
51
 
51
52
  ```
52
53
 
@@ -73,21 +74,21 @@ plt.show()
73
74
 
74
75
  with CodeInterpreter() as sandbox:
75
76
  # you can install dependencies in "jupyter notebook style"
76
- sandbox.exec_cell("!pip install matplotlib")
77
+ sandbox.notebook.exec_cell("!pip install matplotlib")
77
78
 
78
79
  # plot random graph
79
- result = sandbox.exec_cell(code)
80
+ execution = sandbox.notebook.exec_cell(code)
80
81
 
81
- # there's your image
82
- image = result.display_data[0]["image/png"]
82
+ # there's your image
83
+ image = execution.results[0].png
83
84
 
84
- # example how to show the image / prove it works
85
- i = base64.b64decode(image)
86
- i = io.BytesIO(i)
87
- i = mpimg.imread(i, format='PNG')
85
+ # example how to show the image / prove it works
86
+ i = base64.b64decode(image)
87
+ i = io.BytesIO(i)
88
+ i = mpimg.imread(i, format='PNG')
88
89
 
89
- plt.imshow(i, interpolation='nearest')
90
- plt.show()
90
+ plt.imshow(i, interpolation='nearest')
91
+ plt.show()
91
92
  ```
92
93
 
93
94
  ### Streaming code output
@@ -97,14 +98,18 @@ from e2b_code_interpreter import CodeInterpreter
97
98
 
98
99
  code = """
99
100
  import time
101
+ import pandas as pd
100
102
 
101
103
  print("hello")
102
- time.sleep(5)
104
+ time.sleep(3)
105
+ data = pd.DataFrame(data=[[1, 2], [3, 4]], columns=["A", "B"])
106
+ display(data.head(10))
107
+ time.sleep(3)
103
108
  print("world")
104
109
  """
105
110
 
106
111
  with CodeInterpreter() as sandbox:
107
- sandbox.exec_cell(code, on_stdout=print, on_stderr=print)
112
+ sandbox.notebook.exec_cell(code, on_stdout=print, on_stderr=print, on_result=(lambda result: print(result.text)))
108
113
  ```
109
114
 
110
115
  ### Pre-installed Python packages inside the sandbox
@@ -21,10 +21,10 @@ pip install e2b-code-interpreter
21
21
  from e2b_code_interpreter import CodeInterpreter
22
22
 
23
23
  with CodeInterpreter() as sandbox:
24
- sandbox.exec_cell("x = 1")
24
+ sandbox.notebook.exec_cell("x = 1")
25
25
 
26
- result = sandbox.exec_cell("x += 1; x")
27
- print(result.text) # outputs 2
26
+ execution = sandbox.notebook.exec_cell("x+=1; x")
27
+ print(execution.text) # outputs 2
28
28
 
29
29
  ```
30
30
 
@@ -51,21 +51,21 @@ plt.show()
51
51
 
52
52
  with CodeInterpreter() as sandbox:
53
53
  # you can install dependencies in "jupyter notebook style"
54
- sandbox.exec_cell("!pip install matplotlib")
54
+ sandbox.notebook.exec_cell("!pip install matplotlib")
55
55
 
56
56
  # plot random graph
57
- result = sandbox.exec_cell(code)
57
+ execution = sandbox.notebook.exec_cell(code)
58
58
 
59
- # there's your image
60
- image = result.display_data[0]["image/png"]
59
+ # there's your image
60
+ image = execution.results[0].png
61
61
 
62
- # example how to show the image / prove it works
63
- i = base64.b64decode(image)
64
- i = io.BytesIO(i)
65
- i = mpimg.imread(i, format='PNG')
62
+ # example how to show the image / prove it works
63
+ i = base64.b64decode(image)
64
+ i = io.BytesIO(i)
65
+ i = mpimg.imread(i, format='PNG')
66
66
 
67
- plt.imshow(i, interpolation='nearest')
68
- plt.show()
67
+ plt.imshow(i, interpolation='nearest')
68
+ plt.show()
69
69
  ```
70
70
 
71
71
  ### Streaming code output
@@ -75,14 +75,18 @@ from e2b_code_interpreter import CodeInterpreter
75
75
 
76
76
  code = """
77
77
  import time
78
+ import pandas as pd
78
79
 
79
80
  print("hello")
80
- time.sleep(5)
81
+ time.sleep(3)
82
+ data = pd.DataFrame(data=[[1, 2], [3, 4]], columns=["A", "B"])
83
+ display(data.head(10))
84
+ time.sleep(3)
81
85
  print("world")
82
86
  """
83
87
 
84
88
  with CodeInterpreter() as sandbox:
85
- sandbox.exec_cell(code, on_stdout=print, on_stderr=print)
89
+ sandbox.notebook.exec_cell(code, on_stdout=print, on_stderr=print, on_result=(lambda result: print(result.text)))
86
90
  ```
87
91
 
88
92
  ### Pre-installed Python packages inside the sandbox
@@ -0,0 +1,2 @@
1
+ from .main import CodeInterpreter, JupyterExtension
2
+ from .models import Execution, Error, Result, KernelException
@@ -10,8 +10,7 @@ from e2b import EnvVars, ProcessMessage, Sandbox
10
10
  from e2b.constants import TIMEOUT
11
11
 
12
12
  from e2b_code_interpreter.messaging import JupyterKernelWebSocket
13
- from e2b_code_interpreter.models import KernelException, Result
14
-
13
+ from e2b_code_interpreter.models import KernelException, Execution, Result
15
14
 
16
15
  logger = logging.getLogger(__name__)
17
16
 
@@ -66,9 +65,9 @@ class JupyterExtension:
66
65
  kernel_id: Optional[str] = None,
67
66
  on_stdout: Optional[Callable[[ProcessMessage], Any]] = None,
68
67
  on_stderr: Optional[Callable[[ProcessMessage], Any]] = None,
69
- on_display_data: Optional[Callable[[Dict[str, Any]], Any]] = None,
68
+ on_result: Optional[Callable[[Result], Any]] = None,
70
69
  timeout: Optional[float] = TIMEOUT,
71
- ) -> Result:
70
+ ) -> Execution:
72
71
  """
73
72
  Execute code in a notebook cell.
74
73
 
@@ -76,7 +75,7 @@ class JupyterExtension:
76
75
  :param kernel_id: The ID of the kernel to execute the code on. If not provided, the default kernel is used.
77
76
  :param on_stdout: A callback function to handle standard output messages from the code execution.
78
77
  :param on_stderr: A callback function to handle standard error messages from the code execution.
79
- :param on_display_data: A callback function to handle display data messages from the code execution.
78
+ :param on_result: A callback function to handle the result and display calls of the code execution.
80
79
  :param timeout: Timeout for the call
81
80
 
82
81
  :return: Result of the execution
@@ -93,7 +92,7 @@ class JupyterExtension:
93
92
  logger.debug(f"Creating new websocket connection to kernel {kernel_id}")
94
93
  ws = self._connect_to_kernel_ws(kernel_id, timeout=timeout)
95
94
 
96
- session_id = ws.send_execution_message(code, on_stdout, on_stderr, on_display_data)
95
+ session_id = ws.send_execution_message(code, on_stdout, on_stderr, on_result)
97
96
  logger.debug(
98
97
  f"Sent execution message to kernel {kernel_id}, session_id: {session_id}"
99
98
  )
@@ -14,7 +14,7 @@ from e2b.sandbox.websocket_client import WebSocket
14
14
  from e2b.utils.future import DeferredFuture
15
15
  from pydantic import ConfigDict, PrivateAttr, BaseModel
16
16
 
17
- from e2b_code_interpreter.models import Result, Data, Error, MIMEType
17
+ from e2b_code_interpreter.models import Execution, Result, Error
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
@@ -26,21 +26,21 @@ class CellExecution:
26
26
  """
27
27
 
28
28
  input_accepted: bool = False
29
- on_stdout: Optional[Callable[[ProcessMessage], None]] = None
30
- on_stderr: Optional[Callable[[ProcessMessage], None]] = None
31
- on_display_data: Optional[Callable[[Dict[MIMEType, str]], None]] = None
29
+ on_stdout: Optional[Callable[[ProcessMessage], Any]] = None
30
+ on_stderr: Optional[Callable[[ProcessMessage], Any]] = None
31
+ on_result: Optional[Callable[[Result], Any]] = None
32
32
 
33
33
  def __init__(
34
34
  self,
35
- on_stdout: Optional[Callable[[ProcessMessage], None]] = None,
36
- on_stderr: Optional[Callable[[ProcessMessage], None]] = None,
37
- on_display_data: Optional[Callable[[Dict[MIMEType, str]], None]] = None,
35
+ on_stdout: Optional[Callable[[ProcessMessage], Any]] = None,
36
+ on_stderr: Optional[Callable[[ProcessMessage], Any]] = None,
37
+ on_result: Optional[Callable[[Result], Any]] = None,
38
38
  ):
39
- self.partial_result = Result()
40
- self.result = Future()
39
+ self.partial_result = Execution()
40
+ self.execution = Future()
41
41
  self.on_stdout = on_stdout
42
42
  self.on_stderr = on_stderr
43
- self.on_display_data = on_display_data
43
+ self.on_result = on_result
44
44
 
45
45
 
46
46
  class JupyterKernelWebSocket(BaseModel):
@@ -129,9 +129,9 @@ class JupyterKernelWebSocket(BaseModel):
129
129
  def send_execution_message(
130
130
  self,
131
131
  code: str,
132
- on_stdout: Optional[Callable[[ProcessMessage], None]] = None,
133
- on_stderr: Optional[Callable[[ProcessMessage], None]] = None,
134
- on_display_data: Optional[Callable[[Dict[MIMEType, str]], None]] = None,
132
+ on_stdout: Optional[Callable[[ProcessMessage], Any]] = None,
133
+ on_stderr: Optional[Callable[[ProcessMessage], Any]] = None,
134
+ on_result: Optional[Callable[[Result], Any]] = None,
135
135
  ) -> str:
136
136
  message_id = str(uuid.uuid4())
137
137
  logger.debug(f"Sending execution message: {message_id}")
@@ -139,14 +139,16 @@ class JupyterKernelWebSocket(BaseModel):
139
139
  self._cells[message_id] = CellExecution(
140
140
  on_stdout=on_stdout,
141
141
  on_stderr=on_stderr,
142
- on_display_data=on_display_data,
142
+ on_result=on_result,
143
143
  )
144
144
  request = self._get_execute_request(message_id, code)
145
145
  self._queue_in.put(request)
146
146
  return message_id
147
147
 
148
- def get_result(self, message_id: str, timeout: Optional[float] = TIMEOUT) -> Result:
149
- result = self._cells[message_id].result.result(timeout=timeout)
148
+ def get_result(
149
+ self, message_id: str, timeout: Optional[float] = TIMEOUT
150
+ ) -> Execution:
151
+ result = self._cells[message_id].execution.result(timeout=timeout)
150
152
  logger.debug(f"Got result for message: {message_id}")
151
153
  del self._cells[message_id]
152
154
  return result
@@ -167,11 +169,11 @@ class JupyterKernelWebSocket(BaseModel):
167
169
  if not cell:
168
170
  return
169
171
 
170
- result = cell.partial_result
172
+ execution = cell.partial_result
171
173
 
172
174
  if data["msg_type"] == "error":
173
175
  logger.debug(f"Cell {parent_msg_ig} finished execution with error")
174
- result.error = Error(
176
+ execution.error = Error(
175
177
  name=data["content"]["ename"],
176
178
  value=data["content"]["evalue"],
177
179
  traceback_raw=data["content"]["traceback"],
@@ -179,7 +181,7 @@ class JupyterKernelWebSocket(BaseModel):
179
181
 
180
182
  elif data["msg_type"] == "stream":
181
183
  if data["content"]["name"] == "stdout":
182
- result.logs.stdout.append(data["content"]["text"])
184
+ execution.logs.stdout.append(data["content"]["text"])
183
185
  if cell.on_stdout:
184
186
  cell.on_stdout(
185
187
  ProcessMessage(
@@ -189,7 +191,7 @@ class JupyterKernelWebSocket(BaseModel):
189
191
  )
190
192
 
191
193
  elif data["content"]["name"] == "stderr":
192
- result.logs.stderr.append(data["content"]["text"])
194
+ execution.logs.stderr.append(data["content"]["text"])
193
195
  if cell.on_stderr:
194
196
  cell.on_stderr(
195
197
  ProcessMessage(
@@ -200,30 +202,34 @@ class JupyterKernelWebSocket(BaseModel):
200
202
  )
201
203
 
202
204
  elif data["msg_type"] in "display_data":
203
- result.data.append(Data(is_main_result=False, data=data["content"]["data"]))
204
- if cell.on_display_data:
205
- cell.on_display_data(data["content"]["data"])
205
+ result = Result(is_main_result=False, data=data["content"]["data"])
206
+ execution.results.append(result)
207
+ if cell.on_result:
208
+ cell.on_result(result)
206
209
  elif data["msg_type"] == "execute_result":
207
- result.data.append(Data(is_main_result=True, data=data["content"]["data"]))
210
+ result = Result(is_main_result=True, data=data["content"]["data"])
211
+ execution.results.append(result)
212
+ if cell.on_result:
213
+ cell.on_result(result)
208
214
  elif data["msg_type"] == "status":
209
215
  if data["content"]["execution_state"] == "idle":
210
216
  if cell.input_accepted:
211
217
  logger.debug(f"Cell {parent_msg_ig} finished execution")
212
- cell.result.set_result(result)
218
+ cell.execution.set_result(execution)
213
219
 
214
220
  elif data["content"]["execution_state"] == "error":
215
221
  logger.debug(f"Cell {parent_msg_ig} finished execution with error")
216
- result.error = Error(
222
+ execution.error = Error(
217
223
  name=data["content"]["ename"],
218
224
  value=data["content"]["evalue"],
219
225
  traceback_raw=data["content"]["traceback"],
220
226
  )
221
- cell.result.set_result(result)
227
+ cell.execution.set_result(execution)
222
228
 
223
229
  elif data["msg_type"] == "execute_reply":
224
230
  if data["content"]["status"] == "error":
225
231
  logger.debug(f"Cell {parent_msg_ig} finished execution with error")
226
- result.error = Error(
232
+ execution.error = Error(
227
233
  name=data["content"]["ename"],
228
234
  value=data["content"]["evalue"],
229
235
  traceback_raw=data["content"]["traceback"],
@@ -235,8 +241,7 @@ class JupyterKernelWebSocket(BaseModel):
235
241
  logger.debug(f"Input accepted for {parent_msg_ig}")
236
242
  cell.input_accepted = True
237
243
  else:
238
- logger.error(f"[UNHANDLED MESSAGE TYPE]: {data['msg_type']}")
239
- print("[UNHANDLED MESSAGE TYPE]:", data["msg_type"])
244
+ logger.warning(f"[UNHANDLED MESSAGE TYPE]: {data['msg_type']}")
240
245
 
241
246
  def close(self):
242
247
  logger.debug("Closing WebSocket")
@@ -32,11 +32,10 @@ class MIMEType(str):
32
32
  """
33
33
 
34
34
 
35
- class Data:
35
+ class Result:
36
36
  """
37
37
  Represents the data to be displayed as a result of executing a cell in a Jupyter notebook.
38
- This is result returned by ipython kernel: https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics
39
-
38
+ The result is similar to the structure returned by ipython kernel: https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics
40
39
 
41
40
  The result can contain multiple types of data, such as text, images, plots, etc. Each type of data is represented
42
41
  as a string, and the result can contain multiple types of data. The text representation is always present, and
@@ -44,8 +43,9 @@ class Data:
44
43
 
45
44
  The class also provides methods to display the data in a Jupyter notebook.
46
45
  """
46
+
47
47
  text: str
48
- "Text representation of the data. Always present."
48
+ "Text representation of the result. Always present."
49
49
  html: Optional[str] = None
50
50
  markdown: Optional[str] = None
51
51
  svg: Optional[str] = None
@@ -80,13 +80,36 @@ class Data:
80
80
  self.javascript = data.pop("application/javascript", None)
81
81
  self.extra = data
82
82
 
83
- def keys(self) -> Iterable[str]:
84
- """
85
- Returns the MIME types of the data.
86
-
87
- :return: The MIME types of the data.
88
- """
89
- return self.raw.keys()
83
+ def formats(self) -> Iterable[str]:
84
+ """
85
+ Returns all available formats of the result.
86
+
87
+ :return: All available formats of the result in MIME types.
88
+ """
89
+ formats = []
90
+ if self.html:
91
+ formats.append("html")
92
+ if self.markdown:
93
+ formats.append("markdown")
94
+ if self.svg:
95
+ formats.append("svg")
96
+ if self.png:
97
+ formats.append("png")
98
+ if self.jpeg:
99
+ formats.append("jpeg")
100
+ if self.pdf:
101
+ formats.append("pdf")
102
+ if self.latex:
103
+ formats.append("latex")
104
+ if self.json:
105
+ formats.append("json")
106
+ if self.javascript:
107
+ formats.append("javascript")
108
+
109
+ for key in self.extra:
110
+ formats.append(key)
111
+
112
+ return formats
90
113
 
91
114
  def __str__(self) -> str:
92
115
  """
@@ -180,7 +203,7 @@ class Logs(BaseModel):
180
203
  "List of strings printed to stderr by prints, subprocesses, etc."
181
204
 
182
205
 
183
- class Result(BaseModel):
206
+ class Execution(BaseModel):
184
207
  """
185
208
  Represents the result of a cell execution.
186
209
  """
@@ -188,8 +211,8 @@ class Result(BaseModel):
188
211
  class Config:
189
212
  arbitrary_types_allowed = True
190
213
 
191
- data: List[Data] = []
192
- "List of result of the cell (interactively interpreted last line), display calls, e.g. matplotlib plots."
214
+ results: List[Result] = []
215
+ "List of the result of the cell (interactively interpreted last line), display calls (e.g. matplotlib plots)."
193
216
  logs: Logs = Logs()
194
217
  "Logs printed to stdout and stderr during execution."
195
218
  error: Optional[Error] = None
@@ -202,7 +225,7 @@ class Result(BaseModel):
202
225
 
203
226
  :return: The text representation of the result.
204
227
  """
205
- for d in self.data:
228
+ for d in self.results:
206
229
  if d.is_main_result:
207
230
  return d.text
208
231
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "e2b-code-interpreter"
3
- version = "0.0.1a1"
3
+ version = "0.0.2"
4
4
  description = "E2B Code Interpreter - Stateful code execution"
5
5
  authors = ["e2b <hello@e2b.dev>"]
6
6
  license = "Apache-2.0"
@@ -1,2 +0,0 @@
1
- from .main import CodeInterpreter, JupyterExtension
2
- from .models import Result, Error, Data, KernelException