pyodide-code
```py live_pyo id=b26cde2c-5b0b-4acb-814e-9f2b3bdd29a9
print("Hello from Pyodide!")
```
http://localhost:3000
print("Hello from Pyodide!")
Sleep
from time import sleep
for i in range(10):
print(i, i*i, i**3)
sleep(1)
Fibonacci
def fibonacci(n):
if n <= 0:
return []
elif n == 1:
return [0]
elif n == 2:
return [0, 1]
else:
seq = [0, 1]
for i in range(2, n):
seq.append(seq[-1] + seq[-2])
return seq
n = int(input("fib von? "))
print(fibonacci(n))
Mit Memoization
from functools import lru_cache
@lru_cache
def fibonacci(n):
if n in {0, 1}:
return n
return fibonacci(n-1) + fibonacci(n-2)
for n in range(1, 200):
print(f"Fibonacci({n}) = {fibonacci(n)}")
Minimierung mit SciPy
import numpy as np
from scipy import optimize
# Function to minimize: f(x) = (x - 3)^2
def func(x):
return (x - 3) ** 2
# Minimize using scipy.optimize
result = optimize.minimize(func, x0=0)
print("Minimum at x =", result.x[0])
print("Function value at minimum =", result.fun)
Interaktive Komponenten hinzufügen
Das pyodide-code-Package kann erweitert werden - sowohl mit neuen, lokalen Pythin-Bibliotheken als auch mit interaktiven Komponenten, die in React geschrieben sind und mit Python kommunizieren können. Ein Beispiel dafür ist die interaktive Uhr-Komponente:
Interaktive Uhr
<Clock clockId="sbb-uhr" />
```py live_pyo id=d5240fb9-2aab-4e02-8e88-770a117b2068 title=Interaktive_Uhr
from clock import use_clock
from time import sleep
uhr = use_clock("sbb-uhr")
while True:
uhr.set_seconds(uhr.seconds + 1)
sleep(0.01)
```
http://localhost:3000
Uhr: sbb-uhr
from clock import use_clock
from time import sleep
uhr = use_clock("sbb-uhr")
while True:
uhr.set_seconds(uhr.seconds + 1)
if uhr.seconds % 360 == 0:
sleep(0.99)
uhr.set_minutes(uhr.minutes + 6)
if uhr.minutes % 360 == 0:
uhr.set_hours(uhr.hours + 30)
sleep(0.01)
Implementations-Details
Die Implementierung kann im Seitenspezifischen 👉 ./website-Ordner nachvollzogen werden. Die wesentlichen Teile sind:
- Nachrichten, die von Pyodide empfangen werden, werden an den
siteStore#handleMessageweitergeleitet und können so beliebig verarbeitet werden. - Die Python
clock-Bibliothek wird über./website/packages/pyodide-code/pyodideJsModules/siteModules.tsregistriert und stellt dieuse_clock-Funktion zur Verfügung.siteModules.tsimport type { ModuleType } from '@tdev/pyodide-code/pyodideJsModules';/*** this file is to add custom pyodide js modules for the teaching-dev website* ensure to remove this file from the updateTdev.config.yaml to avoid overwriting*/declare module '@tdev/pyodide-code/pyodideJsModules' {export interface MessageTypeMap {clock: {type: 'clock';id: string;clockType: 'hours' | 'minutes' | 'seconds';value: number;timeStamp: number;};}}export const siteModules: Partial<ModuleType> = {clock: (ctx) => {const { sendMessage, getTime } = ctx;return {use_clock: (id: string) => {let hours = 0;let minutes = 0;let seconds = 0;return {get minutes() {return minutes;},get hours() {return hours;},get seconds() {return seconds;},reset: () => {hours = 0;minutes = 0;seconds = 0;['hours', 'minutes', 'seconds'].forEach((clockType) => {sendMessage({type: 'clock',clockType: clockType as 'hours' | 'minutes' | 'seconds',value: 0,id: id,timeStamp: getTime()});});},set_time: (h: number, m: number, s: number) => {hours = h;minutes = m;seconds = s;[['hours', h],['minutes', m],['seconds', s]].forEach(([clockType, value]) => {sendMessage({type: 'clock',clockType: clockType as 'hours' | 'minutes' | 'seconds',value: value as number,id: id,timeStamp: getTime()});});},set_hours: (deg: number) => {hours = deg;sendMessage({type: 'clock',clockType: 'hours',value: deg,id: id,timeStamp: getTime()});},set_minutes: (deg: number) => {minutes = deg;sendMessage({type: 'clock',clockType: 'minutes',value: deg,id: id,timeStamp: getTime()});},set_seconds: (deg: number) => {seconds = deg;sendMessage({type: 'clock',clockType: 'seconds',value: deg,id: id,timeStamp: getTime()});}};}};}}; - In
./website/packages/pyodide-code/models/Clock.tswird ein MobX-Modell für die Uhr definiert. Es kann über@tdev/pyodide-code/models/Clockimportiert und in React-Komponenten verwendet werden. - In
./website/packages/pyodide-code/components/Clockwird die eigentliche Uhr-Komponente implementiert, die dasClock-Modell verwendet, um die Zeiger zu positionieren. - In
./website/stores/ClockStore.tswird ein MobX-Store definiert, der die verschiedenen Uhren verwaltet und die Nachrichten von Pyodide verarbeitet. Dieser Store wird im./website/stores/SiteStore.tsregistriert und initialisiert.
Installation
packages/tdev/pyodide-codeKopiere des packages/tdev/pyodide-code-Verzeichnis in das tdev-website/website/packages-Verzeichnis oder über updateTdev.config.yaml hinzufügen.
- Hinzufügen des
pyodide-code-Package zu denapiDocumentProvidersimsiteConfig.ts:siteConfig.tsconst getSiteConfig: SiteConfigProvider = () => {return {apiDocumentProviders: [require.resolve('@tdev/pyodide-code/register'),]};}; - ServiceWorker hinzufügen:
Damit Eingaben von Benutzer:innen abgewartet werden können, braucht es einen Servie-Worker. Dieser muss direkt in den statischen Ordner 👉static/pyodide.sw.jskopiert werden.static/pyodide.sw.jspyodide.sw.jsconst DOCUSAURUS_SW_SCOPE = '/';const PY_INPUT = 'PY_INPUT';const PY_AWAIT_INPUT = 'PY_AWAIT_INPUT';const PY_STDIN_ROUTE = `${DOCUSAURUS_SW_SCOPE}py-get-input/`;const PY_CANCEL_INPUT = 'PY_CANCEL_INPUT';const PY_CANCEL_ALL = 'PY_CANCEL_ALL';self.addEventListener('install', () => {self.skipWaiting();});self.addEventListener('activate', () => {self.clients.claim();});const resolvers = new Map();self.addEventListener('message', (event) => {switch (event.data.type) {case PY_INPUT:const resolverArray = resolvers.get(event.data.id);const resolver = resolverArray && resolverArray.shift();if (!resolver) {console.error('Error handling input: No resolver');return;}resolver(new Response(event.data.value, { status: 200 }));break;case PY_CANCEL_INPUT:const rejecterArray = resolvers.get(event.data.id);const rejecter = rejecterArray && rejecterArray.shift();if (!rejecter) {console.error('Error handling input: No resolver');return;}rejecter(new Response('Run cancelled', { status: 410 }));break;case PY_CANCEL_ALL:const allResolvers = resolvers.keys();for (const id of allResolvers) {const rejecterArray = resolvers.get(id);if (rejecterArray) {while (rejecterArray.length > 0) {const rejecter = rejecterArray.shift();if (rejecter) {rejecter(new Response('Run cancelled', { status: 410 }));}}}}break;default:return;}});self.addEventListener('fetch', (event) => {const url = new URL(event.request.url);if (url.pathname !== PY_STDIN_ROUTE) {return;}const id = url.searchParams.get('id');if (!id) {console.error('Error handling input: No id');return;}const prompt = url.searchParams.get('prompt');event.waitUntil(self.clients.matchAll().then((clients) => {clients.forEach((client) => {if (client.type === 'window') {client.postMessage({type: PY_AWAIT_INPUT,id,prompt});}});}).catch((err) => console.error('Error matching clients', err)));const promise = new Promise((resolve) => {const resolverArray = resolvers.get(id) || [];resolverArray.push(resolve);resolvers.set(id, resolverArray);});event.respondWith(promise);});
Danach muss erneut installiert werden:
yarn install