@@ -60,12 +60,6 @@ const PyodideWorker = () => {
60
60
61
61
const runPython = async ( python ) => {
62
62
stopped = false ;
63
- await pyodide . loadPackage ( "pyodide_http" ) ;
64
-
65
- await pyodide . runPythonAsync ( `
66
- import pyodide_http
67
- pyodide_http.patch_all()
68
- ` ) ;
69
63
70
64
try {
71
65
await withSupportForPackages ( python , async ( ) => {
@@ -98,6 +92,52 @@ const PyodideWorker = () => {
98
92
await pyodide . loadPackagesFromImports ( python ) ;
99
93
100
94
checkIfStopped ( ) ;
95
+ await pyodide . runPythonAsync (
96
+ `
97
+ import basthon
98
+ import builtins
99
+ import os
100
+
101
+ MAX_FILES = 100
102
+ MAX_FILE_SIZE = 8500000
103
+
104
+ def _custom_open(filename, mode="r", *args, **kwargs):
105
+ if "x" in mode and os.path.exists(filename):
106
+ raise FileExistsError(f"File '{filename}' already exists")
107
+ if ("w" in mode or "a" in mode or "x" in mode) and "b" not in mode:
108
+ if len(os.listdir()) > MAX_FILES and not os.path.exists(filename):
109
+ raise OSError(f"File system limit reached, no more than {MAX_FILES} files allowed")
110
+ class CustomFile:
111
+ def __init__(self, filename):
112
+ self.filename = filename
113
+ self.content = ""
114
+
115
+ def write(self, content):
116
+ self.content += content
117
+ if len(self.content) > MAX_FILE_SIZE:
118
+ raise OSError(f"File '{self.filename}' exceeds maximum file size of {MAX_FILE_SIZE} bytes")
119
+ with _original_open(self.filename, "w") as f:
120
+ f.write(self.content)
121
+ basthon.kernel.write_file({ "filename": self.filename, "content": self.content, "mode": mode })
122
+
123
+ def close(self):
124
+ pass
125
+
126
+ def __enter__(self):
127
+ return self
128
+
129
+ def __exit__(self, exc_type, exc_val, exc_tb):
130
+ self.close()
131
+
132
+ return CustomFile(filename)
133
+ else:
134
+ return _original_open(filename, mode, *args, **kwargs)
135
+
136
+ # Override the built-in open function
137
+ builtins.open = _custom_open
138
+ ` ,
139
+ { filename : "__custom_open__.py" } ,
140
+ ) ;
101
141
await runPythonFn ( ) ;
102
142
103
143
for ( let name of imports ) {
@@ -337,6 +377,12 @@ const PyodideWorker = () => {
337
377
338
378
postMessage ( { method : "handleVisual" , origin, content } ) ;
339
379
} ,
380
+ write_file : ( event ) => {
381
+ const filename = event . toJs ( ) . get ( "filename" ) ;
382
+ const content = event . toJs ( ) . get ( "content" ) ;
383
+ const mode = event . toJs ( ) . get ( "mode" ) ;
384
+ postMessage ( { method : "handleFileWrite" , filename, content, mode } ) ;
385
+ } ,
340
386
locals : ( ) => pyodide . runPython ( "globals()" ) ,
341
387
} ,
342
388
} ;
@@ -346,7 +392,7 @@ const PyodideWorker = () => {
346
392
await pyodide . runPythonAsync ( `
347
393
# Clear all user-defined variables and modules
348
394
for name in dir():
349
- if not name.startswith('_'):
395
+ if not name.startswith('_') and not name=='basthon' :
350
396
del globals()[name]
351
397
` ) ;
352
398
postMessage ( { method : "handleLoaded" , stdinBuffer, interruptBuffer } ) ;
@@ -364,6 +410,8 @@ const PyodideWorker = () => {
364
410
365
411
pyodide = await pyodidePromise ;
366
412
413
+ pyodide . registerJsModule ( "basthon" , fakeBasthonPackage ) ;
414
+
367
415
await pyodide . runPythonAsync ( `
368
416
__old_input__ = input
369
417
def __patched_input__(prompt=False):
@@ -373,6 +421,18 @@ const PyodideWorker = () => {
373
421
__builtins__.input = __patched_input__
374
422
` ) ;
375
423
424
+ await pyodide . runPythonAsync ( `
425
+ import builtins
426
+ # Save the original open function
427
+ _original_open = builtins.open
428
+ ` ) ;
429
+
430
+ await pyodide . loadPackage ( "pyodide-http" ) ;
431
+ await pyodide . runPythonAsync ( `
432
+ import pyodide_http
433
+ pyodide_http.patch_all()
434
+ ` ) ;
435
+
376
436
if ( supportsAllFeatures ) {
377
437
stdinBuffer =
378
438
stdinBuffer || new Int32Array ( new SharedArrayBuffer ( 1024 * 1024 ) ) ; // 1 MiB
@@ -416,6 +476,14 @@ const PyodideWorker = () => {
416
476
417
477
const lines = trace . split ( "\n" ) ;
418
478
479
+ // if the third from last line matches /File "__custom_open__\.py", line (\d+)/g then strip off the last three lines
480
+ if (
481
+ lines . length > 3 &&
482
+ / F i l e " _ _ c u s t o m _ o p e n _ _ \. p y " , l i n e ( \d + ) / g. test ( lines [ lines . length - 3 ] )
483
+ ) {
484
+ lines . splice ( - 3 , 3 ) ;
485
+ }
486
+
419
487
const snippetLine = lines [ lines . length - 2 ] ; // print("hi")invalid
420
488
const caretLine = lines [ lines . length - 1 ] ; // ^^^^^^^
421
489
@@ -424,7 +492,9 @@ const PyodideWorker = () => {
424
492
? [ snippetLine . slice ( 4 ) , caretLine . slice ( 4 ) ] . join ( "\n" )
425
493
: "" ;
426
494
427
- const matches = [ ...trace . matchAll ( / F i l e " ( .* ) " , l i n e ( \d + ) / g) ] ;
495
+ const matches = [
496
+ ...trace . matchAll ( / F i l e " (? ! _ _ c u s t o m _ o p e n _ _ \. p y ) ( .* ) " , l i n e ( \d + ) / g) ,
497
+ ] ;
428
498
const match = matches [ matches . length - 1 ] ;
429
499
430
500
const path = match ? match [ 1 ] : "" ;
0 commit comments