more-compute 0.2.4__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- frontend/app/globals.css +38 -133
- frontend/app/layout.tsx +54 -5
- frontend/components/Notebook.tsx +9 -1
- frontend/components/cell/CellButton.tsx +2 -2
- frontend/components/cell/MonacoCell.tsx +1 -15
- frontend/components/output/CellOutput.tsx +77 -17
- frontend/components/output/ErrorDisplay.tsx +3 -28
- frontend/components/popups/MetricsPopup.tsx +42 -7
- frontend/components/popups/PackagesPopup.tsx +2 -1
- frontend/components/popups/SettingsPopup.tsx +3 -0
- frontend/lib/api.ts +6 -2
- frontend/lib/settings.ts +7 -0
- frontend/lib/websocket-native.ts +3 -0
- frontend/styling_README.md +17 -12
- kernel_run.py +25 -7
- {more_compute-0.2.4.dist-info → more_compute-0.3.0.dist-info}/METADATA +2 -3
- {more_compute-0.2.4.dist-info → more_compute-0.3.0.dist-info}/RECORD +29 -27
- morecompute/__version__.py +1 -1
- morecompute/execution/executor.py +12 -5
- morecompute/execution/worker.py +93 -1
- morecompute/server.py +4 -0
- morecompute/utils/cell_magics.py +713 -0
- morecompute/utils/line_magics.py +949 -0
- morecompute/utils/shell_utils.py +68 -0
- morecompute/utils/special_commands.py +106 -173
- frontend/components/Cell.tsx +0 -383
- {more_compute-0.2.4.dist-info → more_compute-0.3.0.dist-info}/WHEEL +0 -0
- {more_compute-0.2.4.dist-info → more_compute-0.3.0.dist-info}/entry_points.txt +0 -0
- {more_compute-0.2.4.dist-info → more_compute-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {more_compute-0.2.4.dist-info → more_compute-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared utilities for shell command execution.
|
|
3
|
+
Used by both special_commands.py and cell_magics.py to avoid duplication.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import shlex
|
|
7
|
+
import platform
|
|
8
|
+
from typing import List, Dict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def prepare_shell_command(cmd: str) -> List[str]:
|
|
12
|
+
"""
|
|
13
|
+
Convert shell command to subprocess-compatible format.
|
|
14
|
+
Handles pip routing, python unbuffering, and platform-specific shells.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
cmd: Shell command string (e.g., "pip install pandas")
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
List of command arguments for subprocess
|
|
21
|
+
"""
|
|
22
|
+
if cmd.startswith('pip '):
|
|
23
|
+
# Route pip through Python module for better control
|
|
24
|
+
parts = ['python', '-m'] + shlex.split(cmd)
|
|
25
|
+
# Add progress bar control for pip
|
|
26
|
+
if 'install' in cmd and '--progress-bar' not in cmd:
|
|
27
|
+
parts.extend(['--progress-bar', 'off'])
|
|
28
|
+
return parts
|
|
29
|
+
|
|
30
|
+
elif cmd.startswith('python '):
|
|
31
|
+
# Add unbuffered flag to python commands
|
|
32
|
+
parts = shlex.split(cmd)
|
|
33
|
+
parts.insert(1, '-u') # Add -u after 'python'
|
|
34
|
+
return parts
|
|
35
|
+
|
|
36
|
+
else:
|
|
37
|
+
# For other shell commands, use platform-appropriate shell
|
|
38
|
+
system = platform.system()
|
|
39
|
+
if system == 'Windows':
|
|
40
|
+
return ['cmd', '/c', cmd]
|
|
41
|
+
elif system == 'Darwin':
|
|
42
|
+
return ['/bin/bash', '-c', cmd]
|
|
43
|
+
else:
|
|
44
|
+
return ['/bin/bash', '-c', cmd]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def prepare_shell_environment(cmd: str) -> Dict[str, str]:
|
|
48
|
+
"""
|
|
49
|
+
Prepare environment variables for shell command execution.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
cmd: Shell command string
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Environment dictionary
|
|
56
|
+
"""
|
|
57
|
+
env = os.environ.copy()
|
|
58
|
+
|
|
59
|
+
# Always set unbuffered Python
|
|
60
|
+
env['PYTHONUNBUFFERED'] = '1'
|
|
61
|
+
env['PYTHONDONTWRITEBYTECODE'] = '1'
|
|
62
|
+
|
|
63
|
+
# Additional settings for pip commands
|
|
64
|
+
if 'pip install' in cmd:
|
|
65
|
+
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
|
66
|
+
env['PIP_NO_CACHE_DIR'] = '1'
|
|
67
|
+
|
|
68
|
+
return env
|
|
@@ -5,10 +5,15 @@ import asyncio
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import time
|
|
7
7
|
import shlex
|
|
8
|
+
import platform
|
|
8
9
|
from contextlib import redirect_stdout, redirect_stderr
|
|
9
10
|
from typing import Dict, Any, Optional, Tuple, Union
|
|
10
11
|
from fastapi import WebSocket
|
|
11
12
|
|
|
13
|
+
from .cell_magics import CellMagicHandlers
|
|
14
|
+
from .line_magics import LineMagicHandlers
|
|
15
|
+
from .shell_utils import prepare_shell_command, prepare_shell_environment
|
|
16
|
+
|
|
12
17
|
|
|
13
18
|
# this file is not tested that all functions work, need to write a test file / manually check
|
|
14
19
|
# to-do
|
|
@@ -19,14 +24,28 @@ class AsyncSpecialCommandHandler:
|
|
|
19
24
|
def __init__(self, globals_dict: dict):
|
|
20
25
|
self.globals_dict = globals_dict
|
|
21
26
|
self.captured_outputs = {} # Store captured outputs from %%capture
|
|
27
|
+
self.cell_magic_handlers = CellMagicHandlers(globals_dict, self)
|
|
28
|
+
self.line_magic_handlers = LineMagicHandlers(globals_dict)
|
|
22
29
|
|
|
23
30
|
def is_special_command(self, source_code: Union[str, list, tuple]) -> bool:
|
|
24
|
-
"""Check if the source code is a special command"""
|
|
31
|
+
"""Check if the source code is a special command or contains shell commands"""
|
|
25
32
|
text = self._coerce_source_to_text(source_code)
|
|
26
33
|
stripped = text.strip()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
|
|
35
|
+
# Check if starts with magic or shell command
|
|
36
|
+
if (stripped.startswith('!') or
|
|
37
|
+
stripped.startswith('%%') or
|
|
38
|
+
stripped.startswith('%')):
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
# Check if ANY line contains a shell command (like Jupyter/Colab)
|
|
42
|
+
# This allows mixing Python code with !commands
|
|
43
|
+
lines = text.split('\n')
|
|
44
|
+
for line in lines:
|
|
45
|
+
if line.strip().startswith('!'):
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
return False
|
|
30
49
|
|
|
31
50
|
async def execute_special_command(self, source_code: Union[str, list, tuple], result: Dict[str, Any],
|
|
32
51
|
start_time: float, execution_count: int,
|
|
@@ -42,8 +61,27 @@ class AsyncSpecialCommandHandler:
|
|
|
42
61
|
return await self._execute_cell_magic(text, result, start_time, execution_count, websocket)
|
|
43
62
|
elif stripped.startswith('%'):
|
|
44
63
|
return await self._execute_line_magic(stripped[1:], result, start_time, websocket)
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
|
|
65
|
+
# Cell contains shell commands mixed with Python code
|
|
66
|
+
# Treat it like regular code execution but preprocess shell commands
|
|
67
|
+
# This reuses the preprocessing logic from cell_magics.py
|
|
68
|
+
try:
|
|
69
|
+
stdout_text, stderr_text = await self.cell_magic_handlers.execute_cell_content(
|
|
70
|
+
text, result, execution_count, websocket,
|
|
71
|
+
capture_stdout=False, capture_stderr=False
|
|
72
|
+
)
|
|
73
|
+
result["status"] = "ok"
|
|
74
|
+
except Exception as e:
|
|
75
|
+
result["status"] = "error"
|
|
76
|
+
result["error"] = {
|
|
77
|
+
"ename": type(e).__name__,
|
|
78
|
+
"evalue": str(e),
|
|
79
|
+
"traceback": [f"Error executing cell: {str(e)}"]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Calculate execution time
|
|
83
|
+
result["execution_time"] = f"{(time.time() - start_time) * 1000:.1f}ms"
|
|
84
|
+
return result
|
|
47
85
|
|
|
48
86
|
def _coerce_source_to_text(self, source_code: Union[str, list, tuple]) -> str:
|
|
49
87
|
"""Normalize incoming source to a single text string"""
|
|
@@ -61,9 +99,9 @@ class AsyncSpecialCommandHandler:
|
|
|
61
99
|
cell_index: Optional[int] = None) -> Dict[str, Any]:
|
|
62
100
|
"""Execute a shell command with real-time streaming output"""
|
|
63
101
|
try:
|
|
64
|
-
# Prepare environment and command for streaming
|
|
65
|
-
env =
|
|
66
|
-
cmd_parts =
|
|
102
|
+
# Prepare environment and command for streaming (using shared utilities)
|
|
103
|
+
env = prepare_shell_environment(command)
|
|
104
|
+
cmd_parts = prepare_shell_command(command)
|
|
67
105
|
|
|
68
106
|
# Send execution start notification
|
|
69
107
|
if websocket:
|
|
@@ -112,6 +150,8 @@ class AsyncSpecialCommandHandler:
|
|
|
112
150
|
# If pip install/uninstall occurred, notify clients to refresh packages
|
|
113
151
|
try:
|
|
114
152
|
if websocket and (command.startswith('pip install') or command.startswith('pip uninstall') or 'pip install' in command or 'pip uninstall' in command):
|
|
153
|
+
# Small delay to ensure pip finishes writing metadata to disk
|
|
154
|
+
await asyncio.sleep(0.5)
|
|
115
155
|
await websocket.send_json({
|
|
116
156
|
"type": "packages_updated",
|
|
117
157
|
"data": {"action": "pip"}
|
|
@@ -158,57 +198,21 @@ class AsyncSpecialCommandHandler:
|
|
|
158
198
|
# Placeholder for future process-based interruption logic
|
|
159
199
|
return
|
|
160
200
|
|
|
161
|
-
def _prepare_streaming_environment(self, shell_cmd: str) -> dict:
|
|
162
|
-
"""Prepare environment variables for unbuffered output"""
|
|
163
|
-
env = os.environ.copy()
|
|
164
|
-
|
|
165
|
-
# Always set unbuffered Python
|
|
166
|
-
env['PYTHONUNBUFFERED'] = '1'
|
|
167
|
-
env['PYTHONDONTWRITEBYTECODE'] = '1'
|
|
168
|
-
|
|
169
|
-
# Additional settings for specific commands
|
|
170
|
-
if 'pip install' in shell_cmd:
|
|
171
|
-
env['PIP_DISABLE_PIP_VERSION_CHECK'] = '1'
|
|
172
|
-
env['PIP_NO_CACHE_DIR'] = '1'
|
|
173
|
-
|
|
174
|
-
return env
|
|
175
|
-
|
|
176
|
-
def _prepare_command_parts(self, shell_cmd: str) -> list:
|
|
177
|
-
"""Convert shell command to subprocess-compatible format"""
|
|
178
|
-
|
|
179
|
-
if shell_cmd.startswith('pip '):
|
|
180
|
-
# Route pip through Python module for better control
|
|
181
|
-
parts = ['python', '-m'] + shlex.split(shell_cmd)
|
|
182
|
-
# Add progress bar control for pip
|
|
183
|
-
if 'install' in shell_cmd and '--progress-bar' not in shell_cmd:
|
|
184
|
-
parts.extend(['--progress-bar', 'off'])
|
|
185
|
-
return parts
|
|
186
|
-
|
|
187
|
-
elif shell_cmd.startswith('python '):
|
|
188
|
-
# Add unbuffered flag to python commands
|
|
189
|
-
parts = shlex.split(shell_cmd)
|
|
190
|
-
parts.insert(1, '-u') # Add -u after 'python'
|
|
191
|
-
return parts
|
|
192
|
-
|
|
193
|
-
else:
|
|
194
|
-
# For other shell commands, use shell execution
|
|
195
|
-
return ['/bin/zsh', '-c', shell_cmd] # macOS with zsh
|
|
196
|
-
|
|
197
201
|
async def _stream_output(self, stream, stream_type: str, result: Dict[str, Any],
|
|
198
202
|
websocket: Optional[WebSocket] = None,
|
|
199
203
|
cell_index: Optional[int] = None):
|
|
200
204
|
"""Read from a stream and send to websocket, while capturing the output."""
|
|
201
|
-
|
|
205
|
+
|
|
202
206
|
output_text = ""
|
|
203
207
|
while True:
|
|
204
208
|
try:
|
|
205
209
|
line = await stream.readline()
|
|
206
210
|
if not line:
|
|
207
211
|
break
|
|
208
|
-
|
|
212
|
+
|
|
209
213
|
decoded_line = line.decode('utf-8')
|
|
210
214
|
output_text += decoded_line
|
|
211
|
-
|
|
215
|
+
|
|
212
216
|
if websocket:
|
|
213
217
|
await websocket.send_json({
|
|
214
218
|
"type": "stream_output",
|
|
@@ -234,7 +238,7 @@ class AsyncSpecialCommandHandler:
|
|
|
234
238
|
}
|
|
235
239
|
})
|
|
236
240
|
break
|
|
237
|
-
|
|
241
|
+
|
|
238
242
|
# Add the captured text to the final result object
|
|
239
243
|
if output_text:
|
|
240
244
|
# Look for an existing stream output of the same type to append to
|
|
@@ -261,13 +265,37 @@ class AsyncSpecialCommandHandler:
|
|
|
261
265
|
magic_name = magic_parts[0][2:] # Remove %%
|
|
262
266
|
magic_args = magic_parts[1:] if len(magic_parts) > 1 else []
|
|
263
267
|
|
|
268
|
+
# Map magic names to handler methods
|
|
269
|
+
magic_handlers = {
|
|
270
|
+
"capture": lambda: self.cell_magic_handlers.handle_capture(
|
|
271
|
+
magic_args, cell_content, result, start_time, execution_count, websocket
|
|
272
|
+
),
|
|
273
|
+
"time": lambda: self.cell_magic_handlers.handle_time(
|
|
274
|
+
cell_content, result, start_time, execution_count, websocket
|
|
275
|
+
),
|
|
276
|
+
"timeit": lambda: self.cell_magic_handlers.handle_timeit(
|
|
277
|
+
magic_args, cell_content, result, start_time, execution_count, websocket
|
|
278
|
+
),
|
|
279
|
+
"writefile": lambda: self.cell_magic_handlers.handle_writefile(
|
|
280
|
+
magic_args, cell_content, result, start_time, websocket
|
|
281
|
+
),
|
|
282
|
+
"bash": lambda: self.cell_magic_handlers.handle_bash(
|
|
283
|
+
cell_content, result, start_time, websocket
|
|
284
|
+
),
|
|
285
|
+
"sh": lambda: self.cell_magic_handlers.handle_bash(
|
|
286
|
+
cell_content, result, start_time, websocket
|
|
287
|
+
),
|
|
288
|
+
"html": lambda: self.cell_magic_handlers.handle_html(
|
|
289
|
+
cell_content, result, start_time, websocket
|
|
290
|
+
),
|
|
291
|
+
"markdown": lambda: self.cell_magic_handlers.handle_markdown(
|
|
292
|
+
cell_content, result, start_time, websocket
|
|
293
|
+
),
|
|
294
|
+
}
|
|
295
|
+
|
|
264
296
|
try:
|
|
265
|
-
if magic_name
|
|
266
|
-
return await
|
|
267
|
-
elif magic_name == "time":
|
|
268
|
-
return await self._handle_time_magic(cell_content, result, start_time, execution_count, websocket)
|
|
269
|
-
elif magic_name == "writefile":
|
|
270
|
-
return await self._handle_writefile_magic(magic_args, cell_content, result, start_time, websocket)
|
|
297
|
+
if magic_name in magic_handlers:
|
|
298
|
+
return await magic_handlers[magic_name]()
|
|
271
299
|
else:
|
|
272
300
|
result["status"] = "error"
|
|
273
301
|
result["error"] = {
|
|
@@ -310,19 +338,30 @@ class AsyncSpecialCommandHandler:
|
|
|
310
338
|
magic_name = parts[0]
|
|
311
339
|
magic_args = parts[1:] if len(parts) > 1 else []
|
|
312
340
|
|
|
341
|
+
# Map magic names to handler methods
|
|
342
|
+
magic_handlers = {
|
|
343
|
+
"pwd": lambda: self.line_magic_handlers.handle_pwd(magic_args, result, websocket),
|
|
344
|
+
"cd": lambda: self.line_magic_handlers.handle_cd(magic_args, result, websocket),
|
|
345
|
+
"ls": lambda: self.line_magic_handlers.handle_ls(magic_args, result, websocket),
|
|
346
|
+
"env": lambda: self.line_magic_handlers.handle_env(magic_args, result, websocket),
|
|
347
|
+
"who": lambda: self.line_magic_handlers.handle_who(magic_args, result, websocket),
|
|
348
|
+
"whos": lambda: self.line_magic_handlers.handle_whos(magic_args, result, websocket),
|
|
349
|
+
"time": lambda: self.line_magic_handlers.handle_time(magic_args, result, websocket),
|
|
350
|
+
"timeit": lambda: self.line_magic_handlers.handle_timeit(magic_args, result, websocket),
|
|
351
|
+
"pip": lambda: self.line_magic_handlers.handle_pip(magic_args, result, self, websocket),
|
|
352
|
+
"load": lambda: self.line_magic_handlers.handle_load(magic_args, result, websocket),
|
|
353
|
+
"reset": lambda: self.line_magic_handlers.handle_reset(magic_args, result, websocket),
|
|
354
|
+
"lsmagic": lambda: self.line_magic_handlers.handle_lsmagic(magic_args, result, websocket),
|
|
355
|
+
"matplotlib": lambda: self.line_magic_handlers.handle_matplotlib(magic_args, result, websocket),
|
|
356
|
+
"load_ext": lambda: self.line_magic_handlers.handle_load_ext(magic_args, result, websocket),
|
|
357
|
+
"reload_ext": lambda: self.line_magic_handlers.handle_reload_ext(magic_args, result, websocket),
|
|
358
|
+
"unload_ext": lambda: self.line_magic_handlers.handle_unload_ext(magic_args, result, websocket),
|
|
359
|
+
"run": lambda: self.line_magic_handlers.handle_run(magic_args, result, websocket),
|
|
360
|
+
}
|
|
361
|
+
|
|
313
362
|
try:
|
|
314
|
-
if magic_name
|
|
315
|
-
return await
|
|
316
|
-
elif magic_name == "cd":
|
|
317
|
-
return await self._handle_cd_magic(magic_args, result, start_time, websocket)
|
|
318
|
-
elif magic_name == "ls":
|
|
319
|
-
return await self._handle_ls_magic(magic_args, result, start_time, websocket)
|
|
320
|
-
elif magic_name == "env":
|
|
321
|
-
return await self._handle_env_magic(magic_args, result, start_time, websocket)
|
|
322
|
-
elif magic_name == "who":
|
|
323
|
-
return await self._handle_who_magic(result, start_time, websocket)
|
|
324
|
-
elif magic_name == "whos":
|
|
325
|
-
return await self._handle_whos_magic(result, start_time, websocket)
|
|
363
|
+
if magic_name in magic_handlers:
|
|
364
|
+
return await magic_handlers[magic_name]()
|
|
326
365
|
else:
|
|
327
366
|
result["status"] = "error"
|
|
328
367
|
result["error"] = {
|
|
@@ -356,109 +395,3 @@ class AsyncSpecialCommandHandler:
|
|
|
356
395
|
|
|
357
396
|
result["execution_time"] = f"{(time.time() - start_time) * 1000:.1f}ms"
|
|
358
397
|
return result
|
|
359
|
-
|
|
360
|
-
# Cell Magic Implementations
|
|
361
|
-
|
|
362
|
-
async def _handle_capture_magic(self, args: list, cell_content: str, result: Dict[str, Any],
|
|
363
|
-
start_time: float, execution_count: int,
|
|
364
|
-
websocket: Optional[WebSocket] = None) -> Dict[str, Any]:
|
|
365
|
-
"""Handle %%capture magic - capture stdout/stderr without displaying"""
|
|
366
|
-
output_var = args[0] if args else None
|
|
367
|
-
no_stdout = "--no-stdout" in args
|
|
368
|
-
no_stderr = "--no-stderr" in args
|
|
369
|
-
|
|
370
|
-
# Capture outputs
|
|
371
|
-
stdout_capture = None if no_stdout else io.StringIO()
|
|
372
|
-
stderr_capture = None if no_stderr else io.StringIO()
|
|
373
|
-
|
|
374
|
-
try:
|
|
375
|
-
# Execute the cell content with output capture
|
|
376
|
-
if stdout_capture or stderr_capture:
|
|
377
|
-
with redirect_stdout(stdout_capture or sys.stdout), \
|
|
378
|
-
redirect_stderr(stderr_capture or sys.stderr):
|
|
379
|
-
compiled_code = compile(cell_content, '<cell>', 'exec')
|
|
380
|
-
exec(compiled_code, self.globals_dict)
|
|
381
|
-
else:
|
|
382
|
-
compiled_code = compile(cell_content, '<cell>', 'exec')
|
|
383
|
-
exec(compiled_code, self.globals_dict)
|
|
384
|
-
|
|
385
|
-
# Store captured output in a variable if specified
|
|
386
|
-
if output_var:
|
|
387
|
-
captured_data = {
|
|
388
|
-
'stdout': stdout_capture.getvalue() if stdout_capture else '',
|
|
389
|
-
'stderr': stderr_capture.getvalue() if stderr_capture else ''
|
|
390
|
-
}
|
|
391
|
-
self.globals_dict[output_var] = captured_data
|
|
392
|
-
self.captured_outputs[output_var] = captured_data
|
|
393
|
-
|
|
394
|
-
# Don't add outputs to result (they're captured, not displayed)
|
|
395
|
-
if websocket:
|
|
396
|
-
await websocket.send_json({
|
|
397
|
-
"type": "execution_complete",
|
|
398
|
-
"data": {
|
|
399
|
-
"status": "ok",
|
|
400
|
-
"message": "Output captured" + (f" in variable '{output_var}'" if output_var else "")
|
|
401
|
-
}
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
except Exception as e:
|
|
405
|
-
result["status"] = "error"
|
|
406
|
-
result["error"] = {
|
|
407
|
-
"ename": type(e).__name__,
|
|
408
|
-
"evalue": str(e),
|
|
409
|
-
"traceback": [f"Capture magic error: {str(e)}"]
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if websocket:
|
|
413
|
-
await websocket.send_json({
|
|
414
|
-
"type": "execution_error",
|
|
415
|
-
"data": {
|
|
416
|
-
"error": result["error"]
|
|
417
|
-
}
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
return result
|
|
421
|
-
|
|
422
|
-
# Add other magic method implementations here...
|
|
423
|
-
# (Time magic, writefile magic, line magics like pwd, cd, ls, etc.)
|
|
424
|
-
# I'll implement a few key ones to keep this focused:
|
|
425
|
-
|
|
426
|
-
async def _handle_pwd_magic(self, result: Dict[str, Any], start_time: float,
|
|
427
|
-
websocket: Optional[WebSocket] = None) -> Dict[str, Any]:
|
|
428
|
-
"""Handle %pwd magic - print working directory"""
|
|
429
|
-
try:
|
|
430
|
-
pwd = os.getcwd()
|
|
431
|
-
output_data = {
|
|
432
|
-
"output_type": "execute_result",
|
|
433
|
-
"execution_count": None,
|
|
434
|
-
"data": {
|
|
435
|
-
"text/plain": f"'{pwd}'"
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
result["outputs"].append(output_data)
|
|
439
|
-
|
|
440
|
-
if websocket:
|
|
441
|
-
await websocket.send_json({
|
|
442
|
-
"type": "execute_result",
|
|
443
|
-
"data": {
|
|
444
|
-
"data": output_data["data"]
|
|
445
|
-
}
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
except Exception as e:
|
|
449
|
-
result["status"] = "error"
|
|
450
|
-
result["error"] = {
|
|
451
|
-
"ename": type(e).__name__,
|
|
452
|
-
"evalue": str(e),
|
|
453
|
-
"traceback": [f"PWD magic error: {str(e)}"]
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
if websocket:
|
|
457
|
-
await websocket.send_json({
|
|
458
|
-
"type": "execution_error",
|
|
459
|
-
"data": {
|
|
460
|
-
"error": result["error"]
|
|
461
|
-
}
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
return result
|