@@ -60,12 +60,6 @@ const PyodideWorker = () => {
6060
6161 const runPython = async ( python ) => {
6262 stopped = false ;
63- await pyodide . loadPackage ( "pyodide_http" ) ;
64-
65- await pyodide . runPythonAsync ( `
66- import pyodide_http
67- pyodide_http.patch_all()
68- ` ) ;
6963
7064 try {
7165 await withSupportForPackages ( python , async ( ) => {
@@ -98,6 +92,52 @@ const PyodideWorker = () => {
9892 await pyodide . loadPackagesFromImports ( python ) ;
9993
10094 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+ ) ;
101141 await runPythonFn ( ) ;
102142
103143 for ( let name of imports ) {
@@ -337,6 +377,12 @@ const PyodideWorker = () => {
337377
338378 postMessage ( { method : "handleVisual" , origin, content } ) ;
339379 } ,
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+ } ,
340386 locals : ( ) => pyodide . runPython ( "globals()" ) ,
341387 } ,
342388 } ;
@@ -346,7 +392,7 @@ const PyodideWorker = () => {
346392 await pyodide . runPythonAsync ( `
347393 # Clear all user-defined variables and modules
348394 for name in dir():
349- if not name.startswith('_'):
395+ if not name.startswith('_') and not name=='basthon' :
350396 del globals()[name]
351397 ` ) ;
352398 postMessage ( { method : "handleLoaded" , stdinBuffer, interruptBuffer } ) ;
@@ -364,6 +410,8 @@ const PyodideWorker = () => {
364410
365411 pyodide = await pyodidePromise ;
366412
413+ pyodide . registerJsModule ( "basthon" , fakeBasthonPackage ) ;
414+
367415 await pyodide . runPythonAsync ( `
368416 __old_input__ = input
369417 def __patched_input__(prompt=False):
@@ -373,6 +421,18 @@ const PyodideWorker = () => {
373421 __builtins__.input = __patched_input__
374422 ` ) ;
375423
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+
376436 if ( supportsAllFeatures ) {
377437 stdinBuffer =
378438 stdinBuffer || new Int32Array ( new SharedArrayBuffer ( 1024 * 1024 ) ) ; // 1 MiB
@@ -416,6 +476,14 @@ const PyodideWorker = () => {
416476
417477 const lines = trace . split ( "\n" ) ;
418478
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+
419487 const snippetLine = lines [ lines . length - 2 ] ; // print("hi")invalid
420488 const caretLine = lines [ lines . length - 1 ] ; // ^^^^^^^
421489
@@ -424,7 +492,9 @@ const PyodideWorker = () => {
424492 ? [ snippetLine . slice ( 4 ) , caretLine . slice ( 4 ) ] . join ( "\n" )
425493 : "" ;
426494
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+ ] ;
428498 const match = matches [ matches . length - 1 ] ;
429499
430500 const path = match ? match [ 1 ] : "" ;
0 commit comments