more-compute 0.2.6__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/lib/api.ts +6 -2
- frontend/lib/settings.ts +7 -0
- frontend/lib/websocket-native.ts +3 -0
- frontend/styling_README.md +15 -2
- {more_compute-0.2.6.dist-info → more_compute-0.3.0.dist-info}/METADATA +1 -1
- {more_compute-0.2.6.dist-info → more_compute-0.3.0.dist-info}/RECORD +27 -25
- 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.6.dist-info → more_compute-0.3.0.dist-info}/WHEEL +0 -0
- {more_compute-0.2.6.dist-info → more_compute-0.3.0.dist-info}/entry_points.txt +0 -0
- {more_compute-0.2.6.dist-info → more_compute-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {more_compute-0.2.6.dist-info → more_compute-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -11,17 +11,39 @@ import {
|
|
|
11
11
|
|
|
12
12
|
const POLL_MS = 3000;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
interface MetricsPopupProps {
|
|
15
|
+
onClose?: () => void;
|
|
16
|
+
sharedHistory?: MetricsSnapshot[]; // Passed from parent when in persistent mode
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MetricsPopup: React.FC<MetricsPopupProps> = ({ onClose, sharedHistory }) => {
|
|
15
20
|
const [metrics, setMetrics] = useState<MetricsSnapshot | null>(null);
|
|
16
|
-
const [
|
|
21
|
+
const [localHistory, setLocalHistory] = useState<MetricsSnapshot[]>([]);
|
|
17
22
|
const intervalRef = useRef<number | null>(null);
|
|
18
23
|
|
|
24
|
+
// Use shared history if provided (persistent mode), otherwise use local history (on-demand mode)
|
|
25
|
+
const history = sharedHistory ?? localHistory;
|
|
26
|
+
const isPersistentMode = sharedHistory !== undefined;
|
|
27
|
+
|
|
28
|
+
// Update current metrics from shared history
|
|
19
29
|
useEffect(() => {
|
|
30
|
+
if (isPersistentMode && sharedHistory && sharedHistory.length > 0) {
|
|
31
|
+
setMetrics(sharedHistory[sharedHistory.length - 1]);
|
|
32
|
+
}
|
|
33
|
+
}, [isPersistentMode, sharedHistory]);
|
|
34
|
+
|
|
35
|
+
// On-demand mode: poll metrics when popup is open
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (isPersistentMode) {
|
|
38
|
+
// Don't poll in persistent mode - use shared history
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
const load = async () => {
|
|
21
43
|
try {
|
|
22
44
|
const snap = await fetchMetrics();
|
|
23
45
|
setMetrics(snap);
|
|
24
|
-
|
|
46
|
+
setLocalHistory((prev) => {
|
|
25
47
|
const arr = [...prev, snap];
|
|
26
48
|
return arr.slice(-100);
|
|
27
49
|
});
|
|
@@ -32,7 +54,7 @@ const MetricsPopup: React.FC<{ onClose?: () => void }> = ({ onClose }) => {
|
|
|
32
54
|
return () => {
|
|
33
55
|
if (intervalRef.current) window.clearInterval(intervalRef.current);
|
|
34
56
|
};
|
|
35
|
-
}, []);
|
|
57
|
+
}, [isPersistentMode]);
|
|
36
58
|
|
|
37
59
|
const hasGPU = metrics?.gpu && metrics.gpu.length > 0;
|
|
38
60
|
|
|
@@ -137,7 +159,7 @@ const BigValue: React.FC<{ value: string; subtitle?: string }> = ({
|
|
|
137
159
|
);
|
|
138
160
|
|
|
139
161
|
const MiniChart: React.FC<{ data: number[] }> = ({ data }) => {
|
|
140
|
-
const width =
|
|
162
|
+
const width = 100;
|
|
141
163
|
const height = 48;
|
|
142
164
|
const max = Math.max(100, ...data);
|
|
143
165
|
const points = data
|
|
@@ -148,8 +170,21 @@ const MiniChart: React.FC<{ data: number[] }> = ({ data }) => {
|
|
|
148
170
|
})
|
|
149
171
|
.join(" ");
|
|
150
172
|
return (
|
|
151
|
-
<svg
|
|
152
|
-
|
|
173
|
+
<svg
|
|
174
|
+
width="100%"
|
|
175
|
+
height={height}
|
|
176
|
+
viewBox={`0 0 ${width} ${height}`}
|
|
177
|
+
preserveAspectRatio="none"
|
|
178
|
+
className="mini-chart"
|
|
179
|
+
style={{ display: 'block' }}
|
|
180
|
+
>
|
|
181
|
+
<polyline
|
|
182
|
+
points={points}
|
|
183
|
+
fill="none"
|
|
184
|
+
stroke="var(--mc-primary)"
|
|
185
|
+
strokeWidth="2"
|
|
186
|
+
vectorEffect="non-scaling-stroke"
|
|
187
|
+
/>
|
|
153
188
|
</svg>
|
|
154
189
|
);
|
|
155
190
|
};
|
|
@@ -19,7 +19,8 @@ const PackagesPopup: React.FC<PackagesPopupProps> = ({ onClose }) => {
|
|
|
19
19
|
const [query, setQuery] = useState('');
|
|
20
20
|
|
|
21
21
|
useEffect(() => {
|
|
22
|
-
|
|
22
|
+
// Always force refresh on initial load to ensure fresh data
|
|
23
|
+
loadPackages(true);
|
|
23
24
|
const handler = () => loadPackages(true); // Force refresh when packages updated
|
|
24
25
|
if (typeof window !== 'undefined') {
|
|
25
26
|
window.addEventListener('mc:packages-updated', handler as EventListener);
|
frontend/lib/api.ts
CHANGED
|
@@ -51,8 +51,12 @@ export interface PackagesResponse {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export async function fetchInstalledPackages(forceRefresh: boolean = false): Promise<PackageInfo[]> {
|
|
54
|
-
|
|
55
|
-
const
|
|
54
|
+
// Add timestamp to prevent browser caching
|
|
55
|
+
const timestamp = forceRefresh ? `&t=${Date.now()}` : '';
|
|
56
|
+
const url = forceRefresh ? `/api/packages?force_refresh=true${timestamp}` : `/api/packages`;
|
|
57
|
+
const response = await fetch(url, {
|
|
58
|
+
cache: forceRefresh ? 'no-store' : 'default'
|
|
59
|
+
});
|
|
56
60
|
if (!response.ok) {
|
|
57
61
|
throw new Error(`Failed to load packages: ${response.status}`);
|
|
58
62
|
}
|
frontend/lib/settings.ts
CHANGED
|
@@ -106,12 +106,16 @@ export const THEMES: Record<string, ThemeDefinition> = (() => {
|
|
|
106
106
|
return themes;
|
|
107
107
|
})();
|
|
108
108
|
|
|
109
|
+
export type MetricsCollectionMode = 'persistent' | 'on-demand';
|
|
110
|
+
|
|
109
111
|
export type NotebookSettings = {
|
|
110
112
|
theme: keyof typeof THEMES;
|
|
113
|
+
metricsCollectionMode: MetricsCollectionMode;
|
|
111
114
|
};
|
|
112
115
|
|
|
113
116
|
export const DEFAULT_SETTINGS: NotebookSettings = {
|
|
114
117
|
theme: 'light',
|
|
118
|
+
metricsCollectionMode: 'on-demand', // Only collect metrics when popup is open to save memory
|
|
115
119
|
};
|
|
116
120
|
|
|
117
121
|
const STORAGE_KEY = 'morecompute-settings';
|
|
@@ -127,6 +131,9 @@ export function loadSettings(): NotebookSettings {
|
|
|
127
131
|
// Return only valid settings fields (remove old auto_save, font_size, font_family, etc.)
|
|
128
132
|
const cleanSettings: NotebookSettings = {
|
|
129
133
|
theme,
|
|
134
|
+
metricsCollectionMode: parsed.metricsCollectionMode === 'on-demand' || parsed.metricsCollectionMode === 'persistent'
|
|
135
|
+
? parsed.metricsCollectionMode
|
|
136
|
+
: DEFAULT_SETTINGS.metricsCollectionMode,
|
|
130
137
|
};
|
|
131
138
|
|
|
132
139
|
// Save cleaned settings back to localStorage
|
frontend/lib/websocket-native.ts
CHANGED
|
@@ -62,6 +62,9 @@ export class WebSocketService {
|
|
|
62
62
|
case 'execution_start':
|
|
63
63
|
this.emit('execution_start', messageData);
|
|
64
64
|
break;
|
|
65
|
+
case 'heartbeat':
|
|
66
|
+
this.emit('heartbeat', messageData);
|
|
67
|
+
break;
|
|
65
68
|
case 'stream_output':
|
|
66
69
|
this.emit('stream_output', messageData);
|
|
67
70
|
break;
|
frontend/styling_README.md
CHANGED
|
@@ -2,9 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Available themes: `light`, `dark`, `tokyo-night`, `tokyo-night-storm`, `tokyo-night-light`, `night-owl`, `night-owl-light`, `synthwave-84`, `one-dark-pro`
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- light
|
|
6
|
+
- dark
|
|
7
|
+
- tokyo-night
|
|
8
|
+
- tokyo-night-storm
|
|
9
|
+
- tokyo-night-light
|
|
10
|
+
- night-owl
|
|
11
|
+
- night-owl-light
|
|
12
|
+
- synthwave-84
|
|
13
|
+
- one-dark-pro
|
|
14
|
+
|
|
15
|
+
Available metricsCollectionMode: `on-demand` (only collect when popup open), `persistent` (collect continuously in background, uses more memory (obviously)
|
|
16
|
+
|
|
17
|
+
Example settings.json:
|
|
6
18
|
```json
|
|
7
19
|
{
|
|
8
|
-
"theme": "dark"
|
|
20
|
+
"theme": "dark",
|
|
21
|
+
"metricsCollectionMode": "on-demand"
|
|
9
22
|
}
|
|
10
23
|
```
|
|
@@ -10,38 +10,37 @@ frontend/next.config.ts,sha256=OL_rEfTIZxsB_B5R9JX2AxYXgC0Fc_XiDoRlOGEDEpk,368
|
|
|
10
10
|
frontend/package-lock.json,sha256=pGL_J72WHNTz7iLx2MOwHwnDGfbKzN-r_qnnbzfGbEU,334369
|
|
11
11
|
frontend/package.json,sha256=nUVMqlXGT5m431ArOzzs_geSfmq0AKfqUB40zIAeZ5s,1395
|
|
12
12
|
frontend/postcss.config.mjs,sha256=jEBxSXR5tLs0bQ67U7G961Vb62WVEqqg1piNDYPd7tU,105
|
|
13
|
-
frontend/styling_README.md,sha256=
|
|
13
|
+
frontend/styling_README.md,sha256=jvoXKUzJv0rEIU40gz23Z6Xugy4To8P0N9szg_hKrqw,561
|
|
14
14
|
frontend/tailwind.config.ts,sha256=eP9nVaAuyYo46vGQfCyWbo25_pr2hW830fs1Itcix9Q,620
|
|
15
15
|
frontend/tsconfig.json,sha256=7SvBlRBYmuXAlAteRQTGwEE7ooWuNaPUrZ219dOo61E,598
|
|
16
16
|
frontend/app/favicon.ico,sha256=K4rS0zRVqPc2_DqOv48L3qiEitTA20iigzvQ-c13WTI,25931
|
|
17
|
-
frontend/app/globals.css,sha256=
|
|
18
|
-
frontend/app/layout.tsx,sha256=
|
|
17
|
+
frontend/app/globals.css,sha256=zx2VGZhPHqrFSwCsW667gJ80BkEwL8E7J_IJNoMuYGQ,33738
|
|
18
|
+
frontend/app/layout.tsx,sha256=VMkhvePu0hV0w-huEmTocTr7lZ6DF_7eidrhzCCMwzc,7604
|
|
19
19
|
frontend/app/page.tsx,sha256=p-DgDv8xDnwcRfDJY4rtfSQ2VdYwbnP3G-otWqDR1l0,256
|
|
20
|
-
frontend/components/
|
|
21
|
-
frontend/components/Notebook.tsx,sha256=tJt45gyNch3ifWjnKUDoyvik7snrgRqt55gQgiYEnlc,21528
|
|
20
|
+
frontend/components/Notebook.tsx,sha256=JImlThoX4F363yShOqdYDjIIO3i1uqfCLRkDlzurinQ,21851
|
|
22
21
|
frontend/components/cell/AddCellButton.tsx,sha256=ZC2Vck0JIRDxGYhYv3LPYAdKDo13U6008WG_-XoPlIM,1012
|
|
23
|
-
frontend/components/cell/CellButton.tsx,sha256=
|
|
24
|
-
frontend/components/cell/MonacoCell.tsx,sha256=
|
|
22
|
+
frontend/components/cell/CellButton.tsx,sha256=j3lRStluVtRXjZOhi9isS3XKsRmM0WTn5SaFDM6GuYg,1386
|
|
23
|
+
frontend/components/cell/MonacoCell.tsx,sha256=YunwOlyeRsFqx-sHowhctqMV0RmhcQ1glSl5LO0s7NY,23914
|
|
25
24
|
frontend/components/layout/ConnectionBanner.tsx,sha256=-m77wKCFpeRJ_AQwnM38jLwCY5vfpqE846xeXmT3p8A,870
|
|
26
25
|
frontend/components/layout/Sidebar.tsx,sha256=kxgO2mXX11xI6rX478cUqqQ3xB40jlby21y27GTJSLU,1551
|
|
27
26
|
frontend/components/modals/ConfirmModal.tsx,sha256=3WgXy_wmR8FHZPwzMevfZHFXa0blR4v_SbKG3d5McT4,3652
|
|
28
27
|
frontend/components/modals/ErrorModal.tsx,sha256=kkGHQvgyMYlScKwni04-V_Dq6a0-lg0WodglLARR-uA,3536
|
|
29
28
|
frontend/components/modals/SuccessModal.tsx,sha256=7NVg0MFPVvsBGDOHPVyZTNx4kmZLHgxdhZKKtTD9FTU,3385
|
|
30
|
-
frontend/components/output/CellOutput.tsx,sha256=
|
|
31
|
-
frontend/components/output/ErrorDisplay.tsx,sha256=
|
|
29
|
+
frontend/components/output/CellOutput.tsx,sha256=KLRzwEchvBoeoai8TbabKEwO4tHmog3EKeTJAXtmveI,3665
|
|
30
|
+
frontend/components/output/ErrorDisplay.tsx,sha256=kYfip5zikcFIuN7WW47RVjoqwDujC1q_yMbgHrHl0E0,4996
|
|
32
31
|
frontend/components/output/MarkdownRenderer.tsx,sha256=RtZ5yNRxDXIh_NkNsNiy30wMGIW7V1gfhsjecgMdc80,3341
|
|
33
32
|
frontend/components/popups/ComputePopup.tsx,sha256=B7AwwjpJ9_nNI5qSN53jdR2tAulB5yl7spjMXjH4yDM,44021
|
|
34
33
|
frontend/components/popups/FilterPopup.tsx,sha256=4kx9txg8cWeC6XHlh0pu0-BAfZkLTDYEU93fMdzn86M,13818
|
|
35
34
|
frontend/components/popups/FolderPopup.tsx,sha256=V2tDAbztvNIUyPWtFiwjeIoCmFGQyDosQgST_JsAzLo,5215
|
|
36
|
-
frontend/components/popups/MetricsPopup.tsx,sha256=
|
|
37
|
-
frontend/components/popups/PackagesPopup.tsx,sha256=
|
|
35
|
+
frontend/components/popups/MetricsPopup.tsx,sha256=ublYbOQ-pSU_w48F10uFjDYctysEwmri9lrji5YHIC4,6034
|
|
36
|
+
frontend/components/popups/PackagesPopup.tsx,sha256=faeXL7hGFIEUBU6PUrTIr9P5Lw-81GGKWK1ldnIEDcc,3675
|
|
38
37
|
frontend/components/popups/SettingsPopup.tsx,sha256=rGwYxjkADnyTtpP0MjVpFPBsGoT1eENN2z7nWjLuzFE,2773
|
|
39
38
|
frontend/contexts/PodWebSocketContext.tsx,sha256=QyEg1Msyum7h3T6F3j6pn4Ds5sXFT_9WYu5_0YZ2IUI,8256
|
|
40
|
-
frontend/lib/api.ts,sha256=
|
|
39
|
+
frontend/lib/api.ts,sha256=L6Js3K7RcPCLfVVUz-bKX5hLAVFKCfUSHAKSHk2keAk,11026
|
|
41
40
|
frontend/lib/monaco-themes.ts,sha256=jh_pZAmSMKjY_belbMbZX2WpFBN7baRxvJp9shUDYgk,5396
|
|
42
|
-
frontend/lib/settings.ts,sha256=
|
|
41
|
+
frontend/lib/settings.ts,sha256=klHVyB2n-wTc8YrjKCIf2BEmrR-JLVHrRYIXkDxv9-A,6263
|
|
43
42
|
frontend/lib/themes.json,sha256=mk6IGy6o_DCOerBH3QmfXozTHEiy-alsTLTTIaba7No,292018
|
|
44
|
-
frontend/lib/websocket-native.ts,sha256=
|
|
43
|
+
frontend/lib/websocket-native.ts,sha256=KcokVZAl08iWRAcBOfyt3uej352esOa_-YwLk7M5iSU,6046
|
|
45
44
|
frontend/lib/websocket.ts,sha256=V2Y7sktt2dm1G-ZILggcqIWccsh-xoAG9dLHpNVqIqs,4500
|
|
46
45
|
frontend/public/file.svg,sha256=K2eBLDJcGZoCU2zb7qDFk6cvcH0yO3LuPgjbqwZ1O9Q,391
|
|
47
46
|
frontend/public/globe.svg,sha256=thS5vxg5JZV2YayFFJj-HYAp_UOmL7_thvniYkpX588,1035
|
|
@@ -65,17 +64,17 @@ frontend/public/fonts/Fira.ttf,sha256=dbSM4W7Drd9n_EkfXq8P31KuxbjZ1wtuFiZ8aFebvT
|
|
|
65
64
|
frontend/public/fonts/Tiempos.woff2,sha256=h83bJKvAK301wXCMIvK7ZG5j0H2K3tzAfgo0yk4z8OE,13604
|
|
66
65
|
frontend/public/fonts/VeraMono.ttf,sha256=2kKB3H2xej385kpiztkodcWJU0AFXsi6JKORTrl7NJ0,49224
|
|
67
66
|
frontend/types/notebook.ts,sha256=v23RaZe6H3lU5tq6sqnJDPxC2mu0NZFDCJfiN0mgvSs,1359
|
|
68
|
-
more_compute-0.
|
|
67
|
+
more_compute-0.3.0.dist-info/licenses/LICENSE,sha256=0Ot-XIetYt06iay6IhtpJkruD-cLZtjyv7_aIEE-oSc,1073
|
|
69
68
|
morecompute/__init__.py,sha256=pcMVq8Q7qb42AOn7tqgoZJOi3epDDBnEriiv2WVKnXY,87
|
|
70
|
-
morecompute/__version__.py,sha256=
|
|
69
|
+
morecompute/__version__.py,sha256=VrXpHDu3erkzwl_WXrqINBm9xWkcyUy53IQOj042dOs,22
|
|
71
70
|
morecompute/cli.py,sha256=kVvzvPBqF8xO6UuhU_-TBn99nKwJ405R2mAS6zU0KBc,734
|
|
72
71
|
morecompute/notebook.py,sha256=KEcv0eOEh9N7bPVGoRuKJb47G9MmpQ5zz1B6Dm58w18,4651
|
|
73
72
|
morecompute/process_worker.py,sha256=KsE3r-XpkYGuyO4w3t54VKkD51LfNHAZc3TYattMtrg,7185
|
|
74
|
-
morecompute/server.py,sha256=
|
|
73
|
+
morecompute/server.py,sha256=mdN80iBUixq4KUQfu53aoVsNZ6DM9RaBXc-lwt4bxFk,40285
|
|
75
74
|
morecompute/execution/__init__.py,sha256=jPmBmq8BZWbUEY9XFSpqt5FkgX04uNS10WnUlr7Rnms,134
|
|
76
75
|
morecompute/execution/__main__.py,sha256=pAWB_1bn99u8Gb-tVMSMI-NYvbYbDiwbn40L0a0djeA,202
|
|
77
|
-
morecompute/execution/executor.py,sha256=
|
|
78
|
-
morecompute/execution/worker.py,sha256=
|
|
76
|
+
morecompute/execution/executor.py,sha256=BesGdx10HSGj8PyPrekdQZzc0tXoqidznEaFITOq8JM,20554
|
|
77
|
+
morecompute/execution/worker.py,sha256=4yZc_Z4A_nhCv_Lv8MaseSe7KYdXPYK5yDeQeDZoVwM,16004
|
|
79
78
|
morecompute/models/__init__.py,sha256=VLJ5GWi2uTNiZBdvl-ipSbmA6EL8FZHZ5oq-rJmm9z0,640
|
|
80
79
|
morecompute/models/api_models.py,sha256=-ydvi9SeTfdoY9oVPNQS4by-kQGSknx6BHhGD8E2tpo,4553
|
|
81
80
|
morecompute/services/data_manager.py,sha256=c4GKucetMM-VPNbHyzce6bZRvFfmz8kTd5RppLjoLVc,14329
|
|
@@ -86,15 +85,18 @@ morecompute/services/prime_intellect.py,sha256=b705rHv3RPRsgWedRlHwoP_S-TxxZtMSy
|
|
|
86
85
|
morecompute/static/styles.css,sha256=el_NtrUMbAUNSiMVBn1xlG70m3iPv7dyaIbWQMexhsY,19277
|
|
87
86
|
morecompute/utils/__init__.py,sha256=VIxCL3S1pnjEs4cjKGZqZB68_P8FegdeMIqBjJhI5jQ,419
|
|
88
87
|
morecompute/utils/cache_util.py,sha256=lVlXudHvtyvSo_kCSxORJrI85Jod8FrQLbI2f_JOIbA,661
|
|
88
|
+
morecompute/utils/cell_magics.py,sha256=XUxy6lIsrx-9r0mWn0smI1gvT3-kv0rhN5km9cs3D48,27278
|
|
89
89
|
morecompute/utils/config_util.py,sha256=fGTQll7Zh05ZHrW8LuQNTJGziGnIfvKIU3azbrY-I-s,1793
|
|
90
90
|
morecompute/utils/error_utils.py,sha256=e50WLFdD6ngIC30xAgrzdTYtD8tPOIFkKAAh_sPbK0I,11667
|
|
91
|
+
morecompute/utils/line_magics.py,sha256=kTutYBPAWoURY_pk8HXQ38IP712M2rBBfUg3oN8VrP0,33740
|
|
91
92
|
morecompute/utils/notebook_util.py,sha256=3hH94dtXvhizRVTU9a2b38m_51Y4igoXpkjAXUqpVBQ,1353
|
|
92
93
|
morecompute/utils/python_environment_util.py,sha256=l8WWWPwKbypknw8GwL22NXCji5i1FOy1vWG47J6og4g,7441
|
|
93
|
-
morecompute/utils/
|
|
94
|
+
morecompute/utils/shell_utils.py,sha256=fGFLhQLZU-lmMGALbbS-fKPkhQtmMhZ1FkgQ3TeoFhA,1917
|
|
95
|
+
morecompute/utils/special_commands.py,sha256=IyF9MTINMNNo3P_f56Q6yS_P2HNjA9vohYVxEV7KYnc,17331
|
|
94
96
|
morecompute/utils/system_environment_util.py,sha256=32mQRubo0i4X61o-825T7m-eUSidcEp07qkInP1sWZA,4774
|
|
95
97
|
morecompute/utils/zmq_util.py,sha256=tx7-iS04UN69OFtBzkxcEnRhT7xtI9EzRnrZ_nsH_O0,1889
|
|
96
|
-
more_compute-0.
|
|
97
|
-
more_compute-0.
|
|
98
|
-
more_compute-0.
|
|
99
|
-
more_compute-0.
|
|
100
|
-
more_compute-0.
|
|
98
|
+
more_compute-0.3.0.dist-info/METADATA,sha256=86In89hNjbgE5pKBac7gAQ9YKQD-hVbL3b9IecnskMo,3631
|
|
99
|
+
more_compute-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
100
|
+
more_compute-0.3.0.dist-info/entry_points.txt,sha256=xp7z9eRPNRM4oxkZZVlyXkhkSjN1AjoYI_B7qpDJ1bI,49
|
|
101
|
+
more_compute-0.3.0.dist-info/top_level.txt,sha256=Tamm6ADzjwaQa1z27O7Izcyhyt9f0gVjMv1_tC810aI,32
|
|
102
|
+
more_compute-0.3.0.dist-info/RECORD,,
|
morecompute/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -120,10 +120,17 @@ class NextZmqExecutor:
|
|
|
120
120
|
if handler is not None:
|
|
121
121
|
normalized_source = handler._coerce_source_to_text(source_code) # type: ignore[reportPrivateUsage]
|
|
122
122
|
if handler.is_special_command(normalized_source):
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
# Check if this is a PURE special command (starts with !, %%, or %)
|
|
124
|
+
# vs a MIXED command (contains shell commands but also has Python code)
|
|
125
|
+
stripped = normalized_source.strip()
|
|
126
|
+
is_pure_special = (stripped.startswith('!') or
|
|
127
|
+
stripped.startswith('%%') or
|
|
128
|
+
stripped.startswith('%'))
|
|
129
|
+
|
|
130
|
+
# Only execute PURE special commands locally
|
|
131
|
+
# Mixed commands must go to worker for proper streaming
|
|
132
|
+
if not self.is_remote and is_pure_special:
|
|
133
|
+
# Execute pure special command locally
|
|
127
134
|
execution_count = getattr(self, 'execution_count', 0) + 1
|
|
128
135
|
self.execution_count = execution_count
|
|
129
136
|
start_time = time.time()
|
|
@@ -143,7 +150,7 @@ class NextZmqExecutor:
|
|
|
143
150
|
if websocket:
|
|
144
151
|
await websocket.send_json({'type': 'execution_complete', 'data': {'cell_index': cell_index, 'result': result}})
|
|
145
152
|
return result
|
|
146
|
-
# For remote execution, fall through to send via ZMQ
|
|
153
|
+
# For remote execution OR mixed commands, fall through to send via ZMQ
|
|
147
154
|
|
|
148
155
|
execution_count = getattr(self, 'execution_count', 0) + 1
|
|
149
156
|
self.execution_count = execution_count
|
morecompute/execution/worker.py
CHANGED
|
@@ -10,6 +10,91 @@ import matplotlib
|
|
|
10
10
|
matplotlib.use('Agg')
|
|
11
11
|
import matplotlib.pyplot as plt
|
|
12
12
|
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
import shlex
|
|
15
|
+
import platform
|
|
16
|
+
|
|
17
|
+
# Import shared shell command utilities
|
|
18
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
19
|
+
from utils.shell_utils import prepare_shell_command, prepare_shell_environment
|
|
20
|
+
|
|
21
|
+
def _preprocess_shell_commands(code: str) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Preprocess code to transform IPython-style shell commands (!cmd) into Python function calls.
|
|
24
|
+
Returns transformed code with shell commands converted to _run_shell_command() calls.
|
|
25
|
+
"""
|
|
26
|
+
lines = code.split('\n')
|
|
27
|
+
transformed_lines = []
|
|
28
|
+
|
|
29
|
+
for line in lines:
|
|
30
|
+
# Match shell commands: " !pip install pandas"
|
|
31
|
+
shell_match = re.match(r'^(\s*)!(.+)$', line)
|
|
32
|
+
|
|
33
|
+
if shell_match:
|
|
34
|
+
indent = shell_match.group(1)
|
|
35
|
+
shell_cmd = shell_match.group(2).strip()
|
|
36
|
+
# Use repr() for proper escaping
|
|
37
|
+
shell_cmd_repr = repr(shell_cmd)
|
|
38
|
+
# Transform to function call
|
|
39
|
+
transformed = f"{indent}_run_shell_command({shell_cmd_repr})"
|
|
40
|
+
transformed_lines.append(transformed)
|
|
41
|
+
else:
|
|
42
|
+
transformed_lines.append(line)
|
|
43
|
+
|
|
44
|
+
return '\n'.join(transformed_lines)
|
|
45
|
+
|
|
46
|
+
def _inject_shell_command_function(globals_dict: dict):
|
|
47
|
+
"""Inject the _run_shell_command function into globals if not present."""
|
|
48
|
+
if '_run_shell_command' not in globals_dict:
|
|
49
|
+
def _run_shell_command(cmd: str):
|
|
50
|
+
"""Execute a shell command synchronously with streaming output"""
|
|
51
|
+
# Prepare command and environment (using shared utilities)
|
|
52
|
+
shell_cmd = prepare_shell_command(cmd)
|
|
53
|
+
env = prepare_shell_environment(cmd)
|
|
54
|
+
|
|
55
|
+
# Use Popen for real-time streaming
|
|
56
|
+
process = subprocess.Popen(
|
|
57
|
+
shell_cmd,
|
|
58
|
+
stdout=subprocess.PIPE,
|
|
59
|
+
stderr=subprocess.PIPE,
|
|
60
|
+
text=True,
|
|
61
|
+
bufsize=1, # Line buffered
|
|
62
|
+
env=env
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Stream output line by line
|
|
66
|
+
import threading
|
|
67
|
+
|
|
68
|
+
def read_stream(stream, output_type):
|
|
69
|
+
try:
|
|
70
|
+
for line in iter(stream.readline, ''):
|
|
71
|
+
if not line:
|
|
72
|
+
break
|
|
73
|
+
if output_type == 'stdout':
|
|
74
|
+
print(line, end='')
|
|
75
|
+
sys.stdout.flush()
|
|
76
|
+
else:
|
|
77
|
+
print(line, end='', file=sys.stderr)
|
|
78
|
+
sys.stderr.flush()
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
finally:
|
|
82
|
+
stream.close()
|
|
83
|
+
|
|
84
|
+
stdout_thread = threading.Thread(target=read_stream, args=(process.stdout, 'stdout'))
|
|
85
|
+
stderr_thread = threading.Thread(target=read_stream, args=(process.stderr, 'stderr'))
|
|
86
|
+
stdout_thread.daemon = True
|
|
87
|
+
stderr_thread.daemon = True
|
|
88
|
+
stdout_thread.start()
|
|
89
|
+
stderr_thread.start()
|
|
90
|
+
|
|
91
|
+
return_code = process.wait()
|
|
92
|
+
stdout_thread.join()
|
|
93
|
+
stderr_thread.join()
|
|
94
|
+
|
|
95
|
+
return return_code
|
|
96
|
+
|
|
97
|
+
globals_dict['_run_shell_command'] = _run_shell_command
|
|
13
98
|
|
|
14
99
|
def _setup_signals():
|
|
15
100
|
def _handler(signum, frame):
|
|
@@ -232,7 +317,14 @@ def worker_main():
|
|
|
232
317
|
error_payload = None
|
|
233
318
|
start = time.time()
|
|
234
319
|
try:
|
|
235
|
-
|
|
320
|
+
# Preprocess shell commands (!cmd) to Python function calls
|
|
321
|
+
# This allows code like "import os; !pip install pandas" to work
|
|
322
|
+
preprocessed_code = _preprocess_shell_commands(code)
|
|
323
|
+
|
|
324
|
+
# Inject shell command function into globals if needed
|
|
325
|
+
_inject_shell_command_function(g)
|
|
326
|
+
|
|
327
|
+
compiled = compile(preprocessed_code, '<cell>', 'exec')
|
|
236
328
|
exec(compiled, g, l)
|
|
237
329
|
|
|
238
330
|
# Try to evaluate last expression for display (like Jupyter)
|
morecompute/server.py
CHANGED
|
@@ -119,6 +119,10 @@ async def list_installed_packages(force_refresh: bool = False):
|
|
|
119
119
|
global pod_manager
|
|
120
120
|
cache_key = "packages_list"
|
|
121
121
|
|
|
122
|
+
# Clear cache if force refresh is requested
|
|
123
|
+
if force_refresh and cache_key in packages_cache:
|
|
124
|
+
del packages_cache[cache_key]
|
|
125
|
+
|
|
122
126
|
# Check cache first unless force refresh is requested
|
|
123
127
|
if not force_refresh and cache_key in packages_cache:
|
|
124
128
|
return packages_cache[cache_key]
|