@@ -6,7 +6,7 @@ import type { PyodideInterface } from "pyodide";
66import { loadPyodide } from "pyodide" ;
77import { version as pyodideVersion } from "pyodide/package.json" ;
88import type { PyCallable } from "pyodide/ffi" ;
9- import type { WorkerCapabilities } from "./runtime" ;
9+ import type { WorkerAPI , WorkerCapabilities } from "./runtime" ;
1010import type { ReplOutput , UpdatedFile } from "../interface" ;
1111
1212import execfile_py from "./pyodide/execfile.py?raw" ;
@@ -15,7 +15,10 @@ import check_syntax_py from "./pyodide/check_syntax.py?raw";
1515const HOME = `/home/pyodide/` ;
1616
1717let pyodide : PyodideInterface ;
18- let currentOutputCallback : ( ( output : ReplOutput | UpdatedFile ) => void ) | null = null ;
18+ let pendingOutputPromise : Promise < void > [ ] = [ ] ;
19+ let currentOutputCallback :
20+ | ( ( output : ReplOutput | UpdatedFile ) => Promise < void > )
21+ | null = null ;
1922
2023// Helper function to read all files from the Pyodide file system
2124function readAllFiles ( ) : Record < string , string > {
@@ -51,12 +54,22 @@ async function init(
5154 }
5255
5356 pyodide . setStdout ( {
54- batched : ( str : string ) =>
55- currentOutputCallback ?.( { type : "stdout" , message : str } ) ,
57+ batched : ( str : string ) => {
58+ if ( currentOutputCallback ) {
59+ pendingOutputPromise . push (
60+ currentOutputCallback ( { type : "stdout" , message : str } )
61+ ) ;
62+ }
63+ } ,
5664 } ) ;
5765 pyodide . setStderr ( {
58- batched : ( str : string ) =>
59- currentOutputCallback ?.( { type : "stderr" , message : str } ) ,
66+ batched : ( str : string ) => {
67+ if ( currentOutputCallback ) {
68+ pendingOutputPromise . push (
69+ currentOutputCallback ( { type : "stderr" , message : str } )
70+ ) ;
71+ }
72+ } ,
6073 } ) ;
6174
6275 pyodide . setInterruptBuffer ( interruptBuffer ) ;
@@ -66,30 +79,33 @@ async function init(
6679
6780async function runCode (
6881 code : string ,
69- onOutput : ( output : ReplOutput | UpdatedFile ) => void
82+ onOutput : ( output : ReplOutput | UpdatedFile ) => Promise < void >
7083) : Promise < void > {
7184 if ( ! pyodide ) {
7285 throw new Error ( "Pyodide not initialized" ) ;
7386 }
7487 currentOutputCallback = onOutput ;
88+ pendingOutputPromise = [ ] ;
7589 try {
7690 const result = await pyodide . runPythonAsync ( code ) ;
91+ await Promise . all ( pendingOutputPromise ) ;
7792 if ( result !== undefined ) {
78- onOutput ( {
93+ await onOutput ( {
7994 type : "return" ,
8095 message : String ( result ) ,
8196 } ) ;
8297 }
8398 } catch ( e : unknown ) {
8499 console . log ( e ) ;
100+ await Promise . all ( pendingOutputPromise ) ;
85101 if ( e instanceof Error ) {
86102 // エラーがPyodideのTracebackの場合、2行目から<exec>が出てくるまでを隠す
87103 if ( e . name === "PythonError" && e . message . startsWith ( "Traceback" ) ) {
88104 const lines = e . message . split ( "\n" ) ;
89105 const execLineIndex = lines . findIndex ( ( line ) =>
90106 line . includes ( "<exec>" )
91107 ) ;
92- onOutput ( {
108+ await onOutput ( {
93109 type : "error" ,
94110 message : lines
95111 . slice ( 0 , 1 )
@@ -98,13 +114,13 @@ async function runCode(
98114 . trim ( ) ,
99115 } ) ;
100116 } else {
101- onOutput ( {
117+ await onOutput ( {
102118 type : "error" ,
103119 message : `予期せぬエラー: ${ e . message . trim ( ) } ` ,
104120 } ) ;
105121 }
106122 } else {
107- onOutput ( {
123+ await onOutput ( {
108124 type : "error" ,
109125 message : `予期せぬエラー: ${ String ( e ) . trim ( ) } ` ,
110126 } ) ;
@@ -113,19 +129,20 @@ async function runCode(
113129
114130 const updatedFiles = readAllFiles ( ) ;
115131 for ( const [ filename , content ] of Object . entries ( updatedFiles ) ) {
116- onOutput ( { type : "file" , filename, content } ) ;
132+ await onOutput ( { type : "file" , filename, content } ) ;
117133 }
118134}
119135
120136async function runFile (
121137 name : string ,
122138 files : Record < string , string > ,
123- onOutput : ( output : ReplOutput | UpdatedFile ) => void
139+ onOutput : ( output : ReplOutput | UpdatedFile ) => Promise < void >
124140) : Promise < void > {
125141 if ( ! pyodide ) {
126142 throw new Error ( "Pyodide not initialized" ) ;
127143 }
128144 currentOutputCallback = onOutput ;
145+ pendingOutputPromise = [ ] ;
129146 try {
130147 // Use Pyodide FS API to write files to the file system
131148 for ( const filename of Object . keys ( files ) ) {
@@ -136,8 +153,10 @@ async function runFile(
136153
137154 const pyExecFile = pyodide . runPython ( execfile_py ) as PyCallable ;
138155 pyExecFile ( `${ HOME } /${ name } ` ) ;
156+ await Promise . all ( pendingOutputPromise ) ;
139157 } catch ( e : unknown ) {
140158 console . log ( e ) ;
159+ await Promise . all ( pendingOutputPromise ) ;
141160 if ( e instanceof Error ) {
142161 // エラーがPyodideのTracebackの場合、2行目から<exec>が出てくるまでを隠す
143162 // <exec> 自身も隠す
@@ -146,7 +165,7 @@ async function runFile(
146165 const execLineIndex = lines . findLastIndex ( ( line ) =>
147166 line . includes ( "<exec>" )
148167 ) ;
149- onOutput ( {
168+ await onOutput ( {
150169 type : "error" ,
151170 message : lines
152171 . slice ( 0 , 1 )
@@ -155,13 +174,13 @@ async function runFile(
155174 . trim ( ) ,
156175 } ) ;
157176 } else {
158- onOutput ( {
177+ await onOutput ( {
159178 type : "error" ,
160179 message : `予期せぬエラー: ${ e . message . trim ( ) } ` ,
161180 } ) ;
162181 }
163182 } else {
164- onOutput ( {
183+ await onOutput ( {
165184 type : "error" ,
166185 message : `予期せぬエラー: ${ String ( e ) . trim ( ) } ` ,
167186 } ) ;
@@ -170,7 +189,7 @@ async function runFile(
170189
171190 const updatedFiles = readAllFiles ( ) ;
172191 for ( const [ filename , content ] of Object . entries ( updatedFiles ) ) {
173- onOutput ( { type : "file" , filename, content } ) ;
192+ await onOutput ( { type : "file" , filename, content } ) ;
174193 }
175194}
176195
@@ -200,7 +219,7 @@ async function restoreState(): Promise<object> {
200219 throw new Error ( "not implemented" ) ;
201220}
202221
203- const api = {
222+ const api : WorkerAPI = {
204223 init,
205224 runCode,
206225 runFile,
0 commit comments