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';
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;
}
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