@@ -2,7 +2,7 @@ import { useContext, useEffect, useMemo, useRef, useState } from 'react'
2
2
import {
3
3
Packages ,
4
4
PythonContext ,
5
- suppressedMessages ,
5
+ suppressedMessages
6
6
} from '../providers/PythonProvider'
7
7
import { proxy , Remote , wrap } from 'comlink'
8
8
@@ -14,6 +14,10 @@ interface Runner {
14
14
) => Promise < void >
15
15
run : ( code : string ) => Promise < void >
16
16
interruptExecution : ( ) => void
17
+ readFile : ( name : string ) => void
18
+ writeFile : ( name : string , data : string ) => void
19
+ mkdir : ( name : string ) => void
20
+ rmdir : ( name : string ) => void
17
21
}
18
22
19
23
interface UsePythonProps {
@@ -31,12 +35,13 @@ export default function usePython(props?: UsePythonProps) {
31
35
const [ stderr , setStderr ] = useState ( '' )
32
36
const [ pendingCode , setPendingCode ] = useState < string | undefined > ( )
33
37
const [ hasRun , setHasRun ] = useState ( false )
38
+ const [ watchedModules , setWatchedModules ] = useState < Set < string > > ( new Set ( ) )
34
39
35
40
const {
36
41
packages : globalPackages ,
37
42
timeout,
38
43
lazy,
39
- terminateOnCompletion,
44
+ terminateOnCompletion
40
45
} = useContext ( PythonContext )
41
46
42
47
const workerRef = useRef < Worker > ( )
@@ -65,14 +70,14 @@ export default function usePython(props?: UsePythonProps) {
65
70
const official = [
66
71
...new Set ( [
67
72
...( globalPackages . official ?? [ ] ) ,
68
- ...( packages . official ?? [ ] ) ,
69
- ] ) ,
73
+ ...( packages . official ?? [ ] )
74
+ ] )
70
75
]
71
76
const micropip = [
72
77
...new Set ( [
73
78
...( globalPackages . micropip ?? [ ] ) ,
74
- ...( packages . micropip ?? [ ] ) ,
75
- ] ) ,
79
+ ...( packages . micropip ?? [ ] )
80
+ ] )
76
81
]
77
82
return [ official , micropip ]
78
83
} , [ globalPackages , packages ] )
@@ -165,6 +170,18 @@ def run(code, preamble=''):
165
170
print()
166
171
`
167
172
173
+ // prettier-ignore
174
+ const moduleReloadCode = ( modules : Set < string > ) => `
175
+ import importlib
176
+ import sys
177
+ ${ Array . from ( modules ) . map ( ( name ) => `
178
+ if """${ name } """ in sys.modules:
179
+ importlib.reload(sys.modules["""${ name } """])
180
+ ` ) . join ( '' ) }
181
+ del importlib
182
+ del sys
183
+ `
184
+
168
185
const runPython = async ( code : string , preamble = '' ) => {
169
186
// Clear stdout and stderr
170
187
setStdout ( '' )
@@ -201,6 +218,9 @@ def run(code, preamble=''):
201
218
interruptExecution ( )
202
219
} , timeout )
203
220
}
221
+ if ( watchedModules . size > 0 ) {
222
+ await runnerRef . current . run ( moduleReloadCode ( watchedModules ) )
223
+ }
204
224
await runnerRef . current . run ( code )
205
225
// eslint-disable-next-line
206
226
} catch ( error : any ) {
@@ -211,6 +231,32 @@ def run(code, preamble=''):
211
231
}
212
232
}
213
233
234
+ const readFile = ( name : string ) => {
235
+ return runnerRef . current ?. readFile ( name )
236
+ }
237
+
238
+ const writeFile = ( name : string , data : string ) => {
239
+ return runnerRef . current ?. writeFile ( name , data )
240
+ }
241
+
242
+ const mkdir = ( name : string ) => {
243
+ return runnerRef . current ?. mkdir ( name )
244
+ }
245
+
246
+ const rmdir = ( name : string ) => {
247
+ return runnerRef . current ?. rmdir ( name )
248
+ }
249
+
250
+ const watchModules = ( moduleNames : string [ ] ) => {
251
+ setWatchedModules ( ( prev ) => new Set ( [ ...prev , ...moduleNames ] ) )
252
+ }
253
+
254
+ const unwatchModules = ( moduleNames : string [ ] ) => {
255
+ setWatchedModules (
256
+ ( prev ) => new Set ( [ ...prev ] . filter ( ( e ) => ! moduleNames . includes ( e ) ) )
257
+ )
258
+ }
259
+
214
260
const interruptExecution = ( ) => {
215
261
cleanup ( )
216
262
setIsRunning ( false )
@@ -236,5 +282,11 @@ def run(code, preamble=''):
236
282
isLoading,
237
283
isRunning,
238
284
interruptExecution,
285
+ readFile,
286
+ writeFile,
287
+ watchModules,
288
+ unwatchModules,
289
+ mkdir,
290
+ rmdir
239
291
}
240
292
}
0 commit comments