Skip to content

Commit

Permalink
Patch default stdin behaviour (#212)
Browse files Browse the repository at this point in the history
* Revised stdin-patch

* chore(release): 1.9.2-0

* Fixed Safari bug, expose prompt string

* chore(release): 1.9.2-1

* Tidied up messages

* chore(release): 1.9.2-2

* Allow console hook to lazy load

* chore(release): 1.9.2-3

* Updated docs, preinstall pyodide-http

* chore(release): 1.9.2-4

* Vite usage notes

* Docs update

* Fixed sw installation, fixed console behaviour, added loading states

* chore(release): 1.9.2-5

* Prevent echoing input twice

* chore(release): 1.9.2-6

* Minor console styling tweak
  • Loading branch information
elilambnz authored Jul 22, 2023
1 parent 82d14ac commit 337a32b
Show file tree
Hide file tree
Showing 27 changed files with 661 additions and 338 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [1.9.2-6](https://github.com/elilambnz/react-py/compare/v1.9.2-5...v1.9.2-6) (2023-07-22)

### [1.9.2-5](https://github.com/elilambnz/react-py/compare/v1.9.2-4...v1.9.2-5) (2023-07-22)

### [1.9.2-4](https://github.com/elilambnz/react-py/compare/v1.9.2-3...v1.9.2-4) (2023-07-22)

### [1.9.2-3](https://github.com/elilambnz/react-py/compare/v1.9.2-2...v1.9.2-3) (2023-07-22)

### [1.9.2-2](https://github.com/elilambnz/react-py/compare/v1.9.2-1...v1.9.2-2) (2023-07-20)

### [1.9.2-1](https://github.com/elilambnz/react-py/compare/v1.9.2-0...v1.9.2-1) (2023-07-20)

### [1.9.2-0](https://github.com/elilambnz/react-py/compare/v1.9.1...v1.9.2-0) (2023-07-20)

### [1.9.1](https://github.com/elilambnz/react-py/compare/v1.9.0...v1.9.1) (2023-07-18)

## [1.9.0](https://github.com/elilambnz/react-py/compare/v1.8.5...v1.9.0) (2023-07-04)
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Thanks for your interest in contributing to this project! Please take a moment t

**Please ask first before starting work on any significant new features.**

It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create [an issue](https://github.com/elilambnz/react-py/issues/new?labels=enhancement) to first discuss any significant new ideas.
It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors [create an issue](https://github.com/elilambnz/react-py/issues/new?labels=enhancement) to first discuss any significant new ideas.

## Coding standards

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ James Ansley - [James-Ansley](https://github.com/James-Ansley)

## Acknowledgments

This project is heavily based on [Pyodide](https://pyodide.org), a Python distribution for the browser and Node.js based on WebAssembly.
This project uses [Pyodide](https://pyodide.org), a Python distribution for the browser and Node.js based on WebAssembly.

## Contributing

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-py",
"version": "1.9.1",
"version": "1.9.2-6",
"description": "Effortlessly run Python code in your React apps",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
41 changes: 25 additions & 16 deletions src/hooks/usePython.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ interface UsePythonProps {
export default function usePython(props?: UsePythonProps) {
const { packages = {} } = props ?? {}

const [runnerId, setRunnerId] = useState<string>()
const [isLoading, setIsLoading] = useState(false)
const [pyodideVersion, setPyodideVersion] = useState<string | undefined>()
const [isRunning, setIsRunning] = useState(false)
const [output, setOutput] = useState<string[]>([])
const [stdout, setStdout] = useState('')
Expand All @@ -33,7 +33,10 @@ export default function usePython(props?: UsePythonProps) {
packages: globalPackages,
timeout,
lazy,
terminateOnCompletion
terminateOnCompletion,
sendInput,
workerAwaitingInputIds,
getPrompt
} = useContext(PythonContext)

const workerRef = useRef<Worker>()
Expand Down Expand Up @@ -84,7 +87,7 @@ export default function usePython(props?: UsePythonProps) {
return [official, micropip]
}, [globalPackages, packages])

const isReady = !isLoading && !!pyodideVersion
const isReady = !isLoading && !!runnerId

useEffect(() => {
if (workerRef.current && !isReady) {
Expand All @@ -102,9 +105,8 @@ export default function usePython(props?: UsePythonProps) {
}
setOutput((prev) => [...prev, msg])
}),
proxy(({ version }) => {
// The runner is ready once the Pyodide version has been set
setPyodideVersion(version)
proxy(({ id, version }) => {
setRunnerId(id)
console.debug('Loaded pyodide version:', version)
}),
allPackages
Expand Down Expand Up @@ -142,7 +144,7 @@ export default function usePython(props?: UsePythonProps) {
if (terminateOnCompletion && hasRun && !isRunning) {
cleanup()
setIsRunning(false)
setPyodideVersion(undefined)
setRunnerId(undefined)
}
}, [terminateOnCompletion, hasRun, isRunning])

Expand All @@ -151,13 +153,6 @@ import sys
sys.tracebacklimit = 0
import time
def sleep(seconds):
start = now = time.time()
while now - start < seconds:
now = time.time()
time.sleep = sleep
def run(code, preamble=''):
globals_ = {}
try:
Expand Down Expand Up @@ -238,7 +233,7 @@ del sys
const interruptExecution = () => {
cleanup()
setIsRunning(false)
setPyodideVersion(undefined)
setRunnerId(undefined)
setOutput([])

// Spawn new worker
Expand All @@ -253,6 +248,17 @@ del sys
workerRef.current.terminate()
}

const isAwaitingInput =
!!runnerId && workerAwaitingInputIds.includes(runnerId)

const sendUserInput = (value: string) => {
if (!runnerId) {
console.error('No runner id')
return
}
sendInput(runnerId, value)
}

return {
runPython,
stdout,
Expand All @@ -266,6 +272,9 @@ del sys
mkdir,
rmdir,
watchModules,
unwatchModules
unwatchModules,
isAwaitingInput,
sendInput: sendUserInput,
prompt: runnerId ? getPrompt(runnerId) : ''
}
}
63 changes: 52 additions & 11 deletions src/hooks/usePythonConsole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@ interface UsePythonConsoleProps {
export default function usePythonConsole(props?: UsePythonConsoleProps) {
const { packages = {} } = props ?? {}

const [runnerId, setRunnerId] = useState<string>()
const [isLoading, setIsLoading] = useState(false)
const [pyodideVersion, setPyodideVersion] = useState<string | undefined>()
const [banner, setBanner] = useState<string | undefined>()
const [consoleState, setConsoleState] = useState<ConsoleState>()
const [isRunning, setIsRunning] = useState(false)
const [stdout, setStdout] = useState('')
const [stderr, setStderr] = useState('')
const [pendingCode, setPendingCode] = useState<string | undefined>()

const { packages: globalPackages, timeout } = useContext(PythonContext)
const {
packages: globalPackages,
timeout,
lazy,
sendInput,
workerAwaitingInputIds,
getPrompt
} = useContext(PythonContext)

const workerRef = useRef<Worker>()
const runnerRef = useRef<Remote<PythonConsoleRunner>>()
Expand All @@ -52,8 +60,10 @@ export default function usePythonConsole(props?: UsePythonConsoleProps) {
}

useEffect(() => {
// Spawn worker on mount
createWorker()
if (!lazy) {
// Spawn worker on mount
createWorker()
}

// Cleanup worker on unmount
return () => {
Expand All @@ -77,7 +87,7 @@ export default function usePythonConsole(props?: UsePythonConsoleProps) {
return [official, micropip]
}, [globalPackages, packages])

const isReady = !isLoading && !!pyodideVersion && !!banner
const isReady = !isLoading && !!runnerId

useEffect(() => {
if (workerRef.current && !isReady) {
Expand All @@ -97,9 +107,8 @@ export default function usePythonConsole(props?: UsePythonConsoleProps) {
}
setStdout(msg)
}),
proxy(({ version, banner }) => {
// The runner is ready once the Pyodide version has been set
setPyodideVersion(version)
proxy(({ id, version, banner }) => {
setRunnerId(id)
setBanner(banner)
console.debug('Loaded pyodide version:', version)
}),
Expand All @@ -115,6 +124,17 @@ export default function usePythonConsole(props?: UsePythonConsoleProps) {
}
}, [workerRef.current])

// React to ready state and run delayed code if pending
useEffect(() => {
if (pendingCode && isReady) {
const delayedRun = async () => {
await runPython(pendingCode)
setPendingCode(undefined)
}
delayedRun()
}
}, [pendingCode, isReady])

// prettier-ignore
const moduleReloadCode = (modules: Set<string>) => `
import importlib
Expand All @@ -133,6 +153,13 @@ del sys
setStdout('')
setStderr('')

if (lazy && !isReady) {
// Spawn worker and set pending code
createWorker()
setPendingCode(code)
return
}

if (!isReady) {
throw new Error('Pyodide is not loaded yet')
}
Expand Down Expand Up @@ -165,13 +192,13 @@ del sys
clearTimeout(timeoutTimer)
}
},
[isReady, timeout, watchedModules]
[lazy, isReady, timeout, watchedModules]
)

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

Expand All @@ -187,6 +214,17 @@ del sys
workerRef.current.terminate()
}

const isAwaitingInput =
!!runnerId && workerAwaitingInputIds.includes(runnerId)

const sendUserInput = (value: string) => {
if (!runnerId) {
console.error('No runner id')
return
}
sendInput(runnerId, value)
}

return {
runPython,
stdout,
Expand All @@ -202,6 +240,9 @@ del sys
watchModules,
unwatchModules,
banner,
consoleState
consoleState,
isAwaitingInput,
sendInput: sendUserInput,
prompt: runnerId ? getPrompt(runnerId) : ''
}
}
Loading

0 comments on commit 337a32b

Please sign in to comment.