Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/hooks/usePython.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default function usePython(props?: UsePythonProps) {

const workerRef = useRef<Worker>()
const runnerRef = useRef<Remote<PythonRunner>>()
const interruptBuffer = useRef(new Uint8Array(new SharedArrayBuffer(1)))

const {
readFile,
Expand Down Expand Up @@ -111,6 +112,7 @@ export default function usePython(props?: UsePythonProps) {
console.debug('Loaded pyodide version:', version)
}),
'standard',
interruptBuffer.current,
allPackages
)
} catch (error) {
Expand Down Expand Up @@ -200,6 +202,7 @@ del sys
autoImportPackages
)
}
interruptBuffer.current[0] = 0
await runnerRef.current.run(code, autoImportPackages)
// eslint-disable-next-line
} catch (error: any) {
Expand All @@ -213,13 +216,8 @@ del sys
)

const interruptExecution = () => {
cleanup()
setIsRunning(false)
setRunnerId(undefined)
setOutput([])

// Spawn new worker
createWorker()
// 2 stands for SIGINT.
interruptBuffer.current[0] = 2
}

const cleanup = () => {
Expand Down
43 changes: 34 additions & 9 deletions src/hooks/usePythonConsole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface UsePythonConsoleProps {
packages?: Packages
}

export default function usePythonConsole(props?: UsePythonConsoleProps) {
function usePython(props?: UsePythonConsoleProps) {
const { packages = {} } = props ?? {}

const [runnerId, setRunnerId] = useState<string>()
Expand All @@ -43,6 +43,7 @@ export default function usePythonConsole(props?: UsePythonConsoleProps) {

const workerRef = useRef<Worker>()
const runnerRef = useRef<Remote<PythonRunner>>()
const interruptBuffer = useRef(new Uint8Array(new SharedArrayBuffer(1)))

const {
readFile,
Expand Down Expand Up @@ -120,6 +121,7 @@ export default function usePythonConsole(props?: UsePythonConsoleProps) {
console.debug('Loaded pyodide version:', version)
}),
'console',
interruptBuffer.current,
allPackages
)
} catch (error) {
Expand Down Expand Up @@ -191,6 +193,7 @@ del sys
autoImportPackages
)
}
interruptBuffer.current[0] = 0
const runResult = await runnerRef.current.run(code, autoImportPackages)
const { state, error } = runResult ?? {}
setConsoleState(ConsoleState[state as keyof typeof ConsoleState])
Expand All @@ -209,14 +212,8 @@ del sys
)

const interruptExecution = () => {
cleanup()
setIsRunning(false)
setRunnerId(undefined)
setBanner(undefined)
setConsoleState(undefined)

// Spawn new worker
createWorker()
// 2 stands for SIGINT.
interruptBuffer.current[0] = 2
}

const cleanup = () => {
Expand All @@ -241,6 +238,7 @@ del sys
return {
runPython,
stdout,
output,
stderr,
isLoading,
isReady,
Expand All @@ -259,3 +257,30 @@ del sys
prompt: runnerId ? getPrompt(runnerId) : ''
}
}

export default function usePythonConsole(props?: UsePythonConsoleProps) {
const { stderr, stdout, runPython, consoleState, ...pyconsole } =
usePython(props)
const [output, setOutput] = useState([''])
function getPrompt() {
return consoleState === ConsoleState.incomplete ? '... ' : '>>> '
}

function run(input: string) {
setOutput((prev) => [...prev, getPrompt() + input + '\n'])
runPython(input)
}

useEffect(() => {
if (stdout === pyconsole.output.join('')) {
setOutput((prev) => [...prev, stdout, stderr ? stderr + '\n' : ''])
}
}, [stdout, pyconsole.output, stderr])

return {
...pyconsole,
output,
run,
getPrompt
}
}
15 changes: 9 additions & 6 deletions src/providers/PythonProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ const PythonContext = createContext({
export const suppressedMessages = ['Python initialization complete']

interface PythonProviderProps {
workerUrl?: string
packages?: Packages
timeout?: number
lazy?: boolean
terminateOnCompletion?: boolean
autoImportPackages?: boolean
// eslint-disable-next-line
children: any
children: React.ReactNode
}

function PythonProvider(props: PythonProviderProps) {
Expand All @@ -48,10 +48,13 @@ function PythonProvider(props: PythonProviderProps) {
const registerServiceWorker = async () => {
if ('serviceWorker' in navigator) {
try {
const url = new URL('../workers/service-worker', import.meta.url)
const url = new URL(
props.workerUrl ?? '../workers/service-worker',
props.workerUrl ? window.location.origin : import.meta.url
)
const registration = await navigator.serviceWorker.register(url)
if (registration.active) {
console.debug('Service worker active')
console.debug('Service worker active with url', url)
swRef.current = registration.active
}

Expand All @@ -61,7 +64,7 @@ function PythonProvider(props: PythonProviderProps) {
console.debug('Installing new service worker')
installingWorker.addEventListener('statechange', () => {
if (installingWorker.state === 'installed') {
console.debug('New service worker installed')
console.debug('New service worker installed with url', url)
swRef.current = installingWorker
}
})
Expand Down Expand Up @@ -95,6 +98,7 @@ function PythonProvider(props: PythonProviderProps) {
}, [])

const sendInput = (id: string, value: string): void => {
console.debug(`sending input ${id} ${value}`)
if (!workerAwaitingInputIds.has(id)) {
console.error('Worker not awaiting input')
return
Expand Down Expand Up @@ -122,7 +126,6 @@ function PythonProvider(props: PythonProviderProps) {
return next
})
}

return (
<PythonContext.Provider
value={{
Expand Down
3 changes: 3 additions & 0 deletions src/types/Runner.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TypedArray } from 'pyodide'

export interface Runner {
init: (
stdout: (msg: string) => void,
Expand All @@ -11,6 +13,7 @@ export interface Runner {
banner?: string
}) => void,
mode: 'standard' | 'console',
interruptBuffer: TypedArray,
packages?: string[][]
) => Promise<void>
interruptExecution: () => void
Expand Down
17 changes: 13 additions & 4 deletions src/workers/python-worker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
importScripts('https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js')

import { expose } from 'comlink'
import { loadPyodide as loadPyodideType, PyodideInterface } from 'pyodide'
import {
loadPyodide as loadPyodideType,
PyodideInterface,
TypedArray
} from 'pyodide'

declare global {
interface Window {
Expand Down Expand Up @@ -34,7 +38,9 @@ const reactPyModule = {
// Synchronous request to be intercepted by service worker
request.open(
'GET',
`/react-py-get-input/?id=${id}&prompt=${encodeURIComponent(prompt)}`,
`${
location.origin
}/react-py-get-input/?id=${id}&prompt=${encodeURIComponent(prompt)}`,
false
)
request.send(null)
Expand Down Expand Up @@ -70,10 +76,13 @@ const python = {
banner?: string
}) => void,
mode: 'standard' | 'console',
interruptBuffer: TypedArray,
packages?: string[][]
) {
self.pyodide = await self.loadPyodide({ stdout })

self.pyodide.setInterruptBuffer(interruptBuffer)

// Enable debug mode
// self.pyodide.setDebug(true)

Expand All @@ -96,11 +105,11 @@ const python = {

const initCode = `
import sys
import pyodide_http
#import pyodide_http

sys.tracebacklimit = 0

pyodide_http.patch_all()
#pyodide_http.patch_all()
`
await self.pyodide.runPythonAsync(initCode)

Expand Down
Loading