@@ -67,22 +67,8 @@ def do_GET(self) -> bytes | None:
6767 evt.data.print.forEach((x)=>{window.python_logs.push(x)});
6868 }
6969 }
70- function startPyWorker() {
71- window.pyworker_failed = false;
72- window.pyworker = new Worker("worker.js");
73- window.pyworker.onmessage = capturelogs;
74- window.pyworker.onerror = (evt) => {
75- window.pyworker_failed = true;
76- window.python_logs.push(...new TextEncoder().encode(
77- `Worker error: ${evt.message}\n `));
78- if (window.python_done_callback) {
79- let callback = window.python_done_callback;
80- window.python_done_callback = undefined;
81- callback({result: 1});
82- }
83- };
84- }
85- startPyWorker();
70+ window.pyworker = new Worker("worker.js");
71+ window.pyworker.onmessage = capturelogs;
8672 </script>
8773 </head>
8874 <body></body>
@@ -96,28 +82,22 @@ def do_GET(self) -> bytes | None:
9682 elif self .path .endswith ("/worker.js" ):
9783 body = b"""
9884 importScripts("./pyodide.js");
99- function do_print(arg) {
100- let databytes = Array.from(arg);
101- self.postMessage({print:databytes});
102- return databytes.length;
103- }
10485 onmessage = async function (e) {
10586 const data = e.data;
106- try {
107- if (!self.pyodide) {
108- self.pyodide = await loadPyodide();
109- }
110- self.pyodide.setStdout({write:do_print,isatty:data.isatty});
111- self.pyodide.setStderr({write:do_print,isatty:data.isatty});
112-
113- await self.pyodide.loadPackagesFromImports(data.python);
114- let results = await self.pyodide.runPythonAsync(data.python);
115- self.postMessage({results});
116- } catch (error) {
117- do_print(new TextEncoder().encode(
118- `Worker failed: ${error && error.stack || error}\n `));
119- self.postMessage({results: 1});
87+ if (!self.pyodide) {
88+ self.pyodide = await loadPyodide();
89+ }
90+ function do_print(arg) {
91+ let databytes = Array.from(arg);
92+ self.postMessage({print:databytes});
93+ return databytes.length;
12094 }
95+ self.pyodide.setStdout({write:do_print,isatty:data.isatty});
96+ self.pyodide.setStderr({write:do_print,isatty:data.isatty});
97+
98+ await self.pyodide.loadPackagesFromImports(data.python);
99+ let results = await self.pyodide.runPythonAsync(data.python);
100+ self.postMessage({results});
121101 }
122102 """
123103 self .send_response (200 )
@@ -222,78 +202,56 @@ class BrowserDriver:
222202 def __init__ (self , hostname , port , driver ):
223203 self .driver = driver
224204 self .driver .get (f"http://{ hostname } :{ port } /test.html" )
205+ # Chrome on CI takes longer than locally to compile.
206+ self .driver .set_script_timeout (1200 )
225207
226208 def load_pyodide (self , dist_dir ):
227209 pass
228210
229211 def load_arrow (self ):
230- code = (f"import pyodide_js as pjs\n "
231- f"await pjs.loadPackage('{ PYARROW_WHEEL_PATH .name } ')\n " )
232- for attempt in range (3 ):
233- try :
234- result = self .execute_python (code , timeout = 300 )
235- if result :
236- raise RuntimeError (
237- "PyArrow wheel failed to load in browser "
238- f"(exit status { result } ), see log above" )
239- return result
240- except TimeoutError :
241- if attempt == 2 :
242- raise TimeoutError (
243- "Timed out initializing Pyodide/PyArrow in browser" )
244- print ("Timed out initializing Pyodide/loading PyArrow. Restarting" ,
245- flush = True )
246- self .restart_worker ()
247-
248- def execute_python (self , code , wait_for_terminate = True , timeout = None ):
249- self .driver .execute_script (
250- f"""
251- let python = `{ code } `;
252- delete window.python_script_done;
253- window.python_done_callback= (x) => {{window.python_script_done=x;}};
254- window.pyworker.postMessage(
255- {{python,isatty:{ 'true' if sys .stdout .isatty () else 'false' } }});
256- """
212+ self .execute_python (
213+ f"import pyodide_js as pjs\n "
214+ f"await pjs.loadPackage('{ PYARROW_WHEEL_PATH .name } ')\n "
257215 )
258- if wait_for_terminate :
259- return self .wait_for_done (timeout = timeout )
260216
261- def restart_worker (self ):
262- self .driver .execute_script (
263- "window.pyworker.terminate(); startPyWorker(); "
264- "delete window.python_script_done; window.python_done_callback = undefined;"
265- )
217+ def execute_python (self , code , wait_for_terminate = True ):
218+ if wait_for_terminate :
219+ self .driver .execute_async_script (
220+ f"""
221+ let callback = arguments[arguments.length-1];
222+ python = `{ code } `;
223+ window.python_done_callback = callback;
224+ window.pyworker.postMessage(
225+ {{python, isatty: { 'true' if sys .stdout .isatty () else 'false' } }});
226+ """
227+ )
228+ else :
229+ self .driver .execute_script (
230+ f"""
231+ let python = `{ code } `;
232+ window.python_done_callback= (x) => {{window.python_script_done=x;}};
233+ window.pyworker.postMessage(
234+ {{python,isatty:{ 'true' if sys .stdout .isatty () else 'false' } }});
235+ """
236+ )
266237
267238 def clear_logs (self ):
268239 self .driver .execute_script ("window.python_logs = [];" )
269240
270- def wait_for_done (self , timeout = None ):
271- start = last_heartbeat = time .monotonic ()
241+ def wait_for_done (self ):
272242 while True :
273243 # poll for console.log messages from our webworker
274- # (output of pytest), completion and worker death
275- state = self .driver .execute_script (
276- "let logs = window.python_logs; window.python_logs = [];"
277- "return {logs: logs, done: window.python_script_done,"
278- " failed: window.pyworker_failed === true};"
244+ # which are the output of pytest
245+ lines = self .driver .execute_script (
246+ "let temp = window.python_logs;window.python_logs=[];return temp;"
279247 )
280- lines = state .get ("logs" ) or []
281248 if len (lines ) > 0 :
282249 sys .stdout .buffer .write (bytes (lines ))
283- sys .stdout .buffer .flush ()
284- done = state .get ("done" )
250+ done = self .driver .execute_script ("return window.python_script_done;" )
285251 if done is not None :
252+ value = done ["result" ]
286253 self .driver .execute_script ("delete window.python_script_done;" )
287- return done ["result" ]
288- if state .get ("failed" ):
289- raise RuntimeError (
290- "Browser-side worker died. See 'Worker error:' in log above" )
291- now = time .monotonic ()
292- if timeout is not None and now - start > timeout :
293- raise TimeoutError
294- if now - last_heartbeat > 30 :
295- print ("Waiting for browser-side Python to finish..." , flush = True )
296- last_heartbeat = now
254+ return value
297255 time .sleep (0.1 )
298256
299257
@@ -304,7 +262,6 @@ def __init__(self, hostname, port):
304262 options = Options ()
305263 options .add_argument ("--headless" )
306264 options .add_argument ("--no-sandbox" )
307- options .add_argument ("--disable-dev-shm-usage" )
308265 driver = webdriver .Chrome (options = options )
309266 driver .command_executor ._client_config .timeout = 1200
310267 super ().__init__ (hostname , port , driver )
0 commit comments