Package: pyodide-interrupts
pyodide_interruptsThis is a package to allow handling of interrupts inside of Pyodide.
Pyodide does not have preemptive multitasking. This package enables handling keyboard interrupts in Pyodide.This defines one context handlercheck_interrupts(callback, interval)which causescallbackto be called everyintervalinstructions.Simple Example:>>>defcallback():print("check")...withcheck_interrupts(callback,10):...foriinrange(50):...print(i,end=",")0,1,check2,3,4,5,6,check7,8,9,10,11,check12,13,14,15,16,check17,18,19,20,21,check22,23,24,25,26,check27,28,29,30,31,check32,33,34,35,36,check37,38,39,40,41,check42,43,44,45,46,check47,48,49,checkSketch of usageIn real usage, I use the following callback:defcheck_for_interrupt(interrupt_buffer):defhelper():ifinterrupt_buffer()==0:returnraiseKeyboardInterrupt()returnhelperinterrupt_bufferis a javascript wrapper around aSharedArrayBuffer. On the main thread:letuuid=uuid();letinterrupt_buffer=newInt32Array(newSharedArrayBuffer(4));pyodide_worker.postMessage({"cmd":"execute_python",code,interrupt_buffer,uuid});letresult=awaitresponsePromise(uuid);// If user cancels, write a nonzero value into our SAB, this will signal pyodide to quit execution of code.onUserCancel(()=>{interrupt_buffer[0]=2;});On the pyodide worker thread:self.messages={};functionhandleExecutePython(msg){// Wrap interrupt buffer in a function that gets its value// Pyodide Python <==> Javascript bindings don't understand how to get values out of the SAB directly.msg.interrupt_buffer=function(){returnmsg.interrupt_buffer[0];};messages[msg.uuid]=msg;self.pyodide.globals["handle_message"](uuid);}and then the pyodide code:fromjsimportmessages,postMessagedefhandle_message(uuid):msg=dict(messages[uuid])delmessages[uuid]# Here would use msg["cmd"] to look up handling in a dispatch.interrupt_buffer=msg["interrupt_buffer"]# check_for_interrupt will raise a KeyboardInterrupt if "onUserCancel" handler is executed on main thread.withcheck_interrupts(check_for_interrupt(interrupt_buffer),10_000):result=run_code(code)postMessage({"cmd":"execute_pyodide_result","result":result,"uuid":uuid})defrun_code(code):# Parse code into ast, handle errors, get result out, etc hereSecurity requirements forSharedArrayBufferto workI quote fromthe MDN docs for SharedArrayBuffer:As a baseline requirement, your document needs to be in a secure context.For top-level documents, two headers will need to be set to cross-origin isolate your site:Cross-Origin-Opener-Policy with same-origin as value (protects your origin from attackers)
Cross-Origin-Embedder-Policy with require-corp as value (protects victims from your origin)Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpTo check if cross origin isolation has been successful, you can test against the crossOriginIsolated property available to window and worker contextsBuildingTo build a copy for local use, I recommend creating a virtual environment and then usingpip install .in that virtual environment.
To upload to pypi, we must build the package for amanylinuxABI to insure that the binaries will be compatible with most systems.
Themanylinuxrepository provides docker images with the appropriate old versions of CentOS for us to use to build these. To build, runsudo ./docker_build_wheels.sh.Warning:This will download a ~300mb docker image the first time you do it. Note that you will need to have docker installed for this to work.
The resulting wheels will end up in thedistdirectory and will be suitable for upload to pypi.[0.1.0] (2020-07-25)