Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pyodide headers #1104

Merged
merged 9 commits into from
Oct 17, 2024
Merged
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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ REACT_APP_PLAUSIBLE_DATA_DOMAIN=''
REACT_APP_PLAUSIBLE_SOURCE=''
REACT_APP_SENTRY_DSN=''
REACT_APP_SENTRY_ENV='local'
PUBLIC_URL='http://localhost:3010'
PUBLIC_URL='http://localhost:3011'
ASSETS_URL='http://localhost:3010'
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3010",
"url": "http://localhost:3011",
"webRoot": "${workspaceFolder}"
}
]
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Added

- PyodideWorker setup for the editor (#1104)
- Enabling `pyodide` support in the web component (#1090)
- `Pyodide` `matplotlib` support (#1087)

Expand Down
File renamed without changes.
12 changes: 7 additions & 5 deletions src/PyodideWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// Nest the PyodideWorker function inside a globalThis object so we control when its initialised.
const PyodideWorker = () => {
// Import scripts dynamically based on the environment
importScripts(`${process.env.PUBLIC_URL}/_internal_sense_hat.js`);
importScripts(`${process.env.PUBLIC_URL}/pygal.js`);
importScripts(
`${process.env.ASSETS_URL}/pyodide/shims/_internal_sense_hat.js`,
);
importScripts(`${process.env.ASSETS_URL}/pyodide/shims/pygal.js`);
importScripts("https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js");

const supportsAllFeatures = typeof SharedArrayBuffer !== "undefined";
Expand Down Expand Up @@ -164,7 +166,7 @@ const PyodideWorker = () => {
before: async () => {
pyodide.registerJsModule("basthon", fakeBasthonPackage);
await pyodide.loadPackage(
`${process.env.ASSETS_URL}/packages/turtle-0.0.1-py3-none-any.whl`,
`${process.env.ASSETS_URL}/pyodide/packages/turtle-0.0.1-py3-none-any.whl`,
Comment on lines -167 to +169
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this one ASSETS_URL when the ones above are PUBLIC_URL? I've gotten a bit confused about what the difference is between the two vars to be honest with you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah they can both probably be merged into one env var now. When the site was deployed and assets were in the bucket but on the same domain it was causing a problem so I exposed the bucket via a URL and hit that directly a while back. But now its all bucket based

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changing them all to ASSETS_URL in this file then we can remove that later if we want to 👍

);
},
after: () =>
Expand All @@ -182,7 +184,7 @@ const PyodideWorker = () => {
pyodide.registerJsModule("basthon", fakeBasthonPackage);
await pyodide.loadPackage([
"setuptools",
`${process.env.ASSETS_URL}/packages/p5-0.0.1-py3-none-any.whl`,
`${process.env.ASSETS_URL}/pyodide/packages/p5-0.0.1-py3-none-any.whl`,
]);
},
after: () => {},
Expand All @@ -203,7 +205,7 @@ const PyodideWorker = () => {
});
await pyodide.loadPackage([
"pillow",
`${process.env.ASSETS_URL}/packages/sense_hat-0.0.1-py3-none-any.whl`,
`${process.env.ASSETS_URL}/pyodide/packages/sense_hat-0.0.1-py3-none-any.whl`,
]);

_internal_sense_hat.config.pyodide = pyodide;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/* eslint import/no-webpack-loader-syntax: off */
/* eslint-disable react-hooks/exhaustive-deps */

import "../../../../../assets/stylesheets/PythonRunner.scss";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
Expand All @@ -20,27 +18,25 @@ import OutputViewToggle from "../OutputViewToggle";
import { SettingsContext } from "../../../../../utils/settings";
import RunnerControls from "../../../../RunButton/RunnerControls";

const PyodideRunner = ({ active }) => {
const getWorkerURL = (url) => {
const content = `
/* global PyodideWorker */
console.log("Worker loading");
importScripts("${url}");
const pyodide = PyodideWorker();
console.log("Worker loaded");
`;
const blob = new Blob([content], { type: "application/javascript" });
return URL.createObjectURL(blob);
};
const getWorkerURL = (url) => {
const content = `
/* global PyodideWorker */
console.log("Worker loading");
importScripts("${url}");
const pyodide = PyodideWorker();
console.log("Worker loaded");
`;
const blob = new Blob([content], { type: "application/javascript" });
return URL.createObjectURL(blob);
};

const workerUrl = getWorkerURL(`${process.env.PUBLIC_URL}/PyodideWorker.js`);
const PyodideRunner = (props) => {
const { active } = props;

// Blob approach + targeted headers - no errors but headers required in host app to interrupt code
const workerUrl = getWorkerURL(`${process.env.PUBLIC_URL}/PyodideWorker.js`);
const pyodideWorker = useMemo(() => new Worker(workerUrl), []);

if (!pyodideWorker) {
console.error("PyodideWorker is not initialized");
}

const interruptBuffer = useRef();
const stdinBuffer = useRef();
const stdinClosed = useRef();
Expand Down Expand Up @@ -312,6 +308,11 @@ const PyodideRunner = ({ active }) => {
}
};

if (!pyodideWorker) {
console.error("PyodideWorker is not initialized");
return;
}

return (
<div
className={`pythonrunner-container pyodiderunner${
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ const SkulptRunner = ({ active, outputPanels = ["text", "visual"] }) => {
scriptElement.type = "text/javascript";
scriptElement.src = path;
scriptElement.async = true;
scriptElement.crossOrigin = "";
scriptElement.onload = function () {
resolve(true);
};
Expand Down
2 changes: 1 addition & 1 deletion src/utils/externalLinkHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useDispatch } from "react-redux";
import { setError, triggerCodeRun } from "../redux/EditorSlice";

const domain = "https://rpf.io/";
const host = process.env.PUBLIC_URL || "http://localhost:3010";
const host = process.env.PUBLIC_URL || "http://localhost:3011";
const rpfDomain = new RegExp(`^${domain}`);
const hostDomain = new RegExp(`^${host}`);
const allowedInternalLinks = [new RegExp(`^#[a-zA-Z0-9]+`)];
Expand Down
26 changes: 26 additions & 0 deletions webpack.component.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ const Dotenv = require("dotenv-webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WorkerPlugin = require("worker-plugin");

let publicUrl = process.env.PUBLIC_URL || "/";
if (!publicUrl.endsWith("/")) {
publicUrl += "/";
}

module.exports = {
entry: {
"web-component": path.resolve(__dirname, "./src/web-component.js"),
Expand Down Expand Up @@ -73,6 +78,8 @@ module.exports = {
output: {
path: path.resolve(__dirname, "./build"),
filename: "[name].js",
publicPath: publicUrl,
workerPublicPath: publicUrl,
},
devServer: {
host: "0.0.0.0",
Expand All @@ -88,6 +95,25 @@ module.exports = {
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"X-Requested-With, content-type, Authorization",
// Pyodide - required for input and code interruption - needed on the host app
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
Comment on lines +98 to +100
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So are we just assuming that host pages will have to add these themselves for now, or am I not understanding?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea that's right. So on editor.raspberrypi.org we've already got these set 👍

},
setupMiddlewares: (middlewares, devServer) => {
devServer.app.use((req, res, next) => {
// PyodideWorker scripts - cross origin required on scripts needed for importScripts
if (
[
"/pyodide/shims/_internal_sense_hat.js",
"/pyodide/shims/pygal.js",
"/PyodideWorker.js",
].includes(req.url)
) {
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
}
next();
});
return middlewares;
},
},
plugins: [
Expand Down
Loading