Skip to content

Commit fc53356

Browse files
Introducing <script type="py-game"> (#2265)
* Introducing <script type="py-game"> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 5be9945 commit fc53356

File tree

12 files changed

+789
-201
lines changed

12 files changed

+789
-201
lines changed

core/package-lock.json

+245-185
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/package.json

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pyscript/core",
3-
"version": "0.6.22",
3+
"version": "0.6.23",
44
"type": "module",
55
"description": "PyScript",
66
"module": "./index.js",
@@ -63,37 +63,37 @@
6363
"add-promise-listener": "^0.1.3",
6464
"basic-devtools": "^0.1.6",
6565
"polyscript": "^0.16.10",
66-
"sabayon": "^0.6.1",
66+
"sabayon": "^0.6.6",
6767
"sticky-module": "^0.1.1",
6868
"to-json-callback": "^0.1.1",
6969
"type-checked-collections": "^0.1.7"
7070
},
7171
"devDependencies": {
72-
"@codemirror/commands": "^6.7.1",
73-
"@codemirror/lang-python": "^6.1.6",
74-
"@codemirror/language": "^6.10.6",
75-
"@codemirror/state": "^6.4.1",
76-
"@codemirror/view": "^6.35.0",
72+
"@codemirror/commands": "^6.8.0",
73+
"@codemirror/lang-python": "^6.1.7",
74+
"@codemirror/language": "^6.10.8",
75+
"@codemirror/state": "^6.5.2",
76+
"@codemirror/view": "^6.36.2",
7777
"@playwright/test": "1.45.3",
78-
"@rollup/plugin-commonjs": "^28.0.1",
79-
"@rollup/plugin-node-resolve": "^15.3.0",
78+
"@rollup/plugin-commonjs": "^28.0.2",
79+
"@rollup/plugin-node-resolve": "^16.0.0",
8080
"@rollup/plugin-terser": "^0.4.4",
8181
"@webreflection/toml-j0.4": "^1.1.3",
8282
"@xterm/addon-fit": "^0.10.0",
8383
"@xterm/addon-web-links": "^0.11.0",
8484
"@xterm/xterm": "^5.5.0",
85-
"bun": "^1.1.38",
86-
"chokidar": "^4.0.1",
85+
"bun": "^1.2.2",
86+
"chokidar": "^4.0.3",
8787
"codedent": "^0.1.2",
8888
"codemirror": "^6.0.1",
89-
"eslint": "^9.16.0",
89+
"eslint": "^9.19.0",
9090
"flatted": "^3.3.2",
91-
"rollup": "^4.28.1",
91+
"rollup": "^4.34.2",
9292
"rollup-plugin-postcss": "^4.0.2",
9393
"rollup-plugin-string": "^3.0.0",
9494
"static-handler": "^0.5.3",
9595
"string-width": "^7.2.0",
96-
"typescript": "^5.7.2",
96+
"typescript": "^5.7.3",
9797
"xterm-readline": "^1.1.2"
9898
},
9999
"repository": {

core/src/config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const badURL = (url, expected = "") => {
2525
* @param {string?} type the optional type to enforce
2626
* @returns {{json: boolean, toml: boolean, text: string}}
2727
*/
28-
const configDetails = async (config, type) => {
28+
export const configDetails = async (config, type) => {
2929
let text = config?.trim();
3030
// we only support an object as root config
3131
let url = "",

core/src/plugins.js

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ export default {
2525
/* webpackIgnore: true */
2626
"./plugins/py-editor.js"
2727
),
28+
["py-game"]: () =>
29+
import(
30+
/* webpackIgnore: true */
31+
"./plugins/py-game.js"
32+
),
2833
["py-terminal"]: () =>
2934
import(
3035
/* webpackIgnore: true */

core/src/plugins/py-game.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { dedent, define } from "polyscript/exports";
2+
3+
import { stdlib } from "../core.js";
4+
import { configDetails } from "../config.js";
5+
import { getText } from "../fetch.js";
6+
7+
let toBeWarned = true;
8+
9+
const hooks = {
10+
main: {
11+
onReady: async (wrap, script) => {
12+
if (toBeWarned) {
13+
toBeWarned = false;
14+
console.warn("⚠️ EXPERIMENTAL `py-game` FEATURE");
15+
}
16+
if (script.hasAttribute("config")) {
17+
const value = script.getAttribute("config");
18+
const { json, toml, text } = configDetails(value);
19+
let config = {};
20+
if (json) config = JSON.parse(text);
21+
else if (toml) {
22+
const { parse } = await import(
23+
/* webpackIgnore: true */ "../3rd-party/toml.js"
24+
);
25+
config = parse(text);
26+
}
27+
if (config.packages) {
28+
const micropip = wrap.interpreter.pyimport("micropip");
29+
await micropip.install(config.packages, {
30+
keep_going: true,
31+
});
32+
micropip.destroy();
33+
}
34+
}
35+
36+
wrap.interpreter.registerJsModule("_pyscript", {
37+
PyWorker() {
38+
throw new Error(
39+
"Unable to use PyWorker in py-game scripts",
40+
);
41+
},
42+
js_import: (...urls) =>
43+
Promise.all(urls.map((url) => import(url))),
44+
get target() {
45+
return script.id;
46+
},
47+
});
48+
49+
await wrap.interpreter.runPythonAsync(stdlib);
50+
51+
let code = dedent(script.textContent);
52+
if (script.src) code = await fetch(script.src).then(getText);
53+
54+
const target = script.getAttribute("target") || "canvas";
55+
const canvas = document.getElementById(target);
56+
wrap.interpreter.canvas.setCanvas2D(canvas);
57+
await wrap.interpreter.runPythonAsync(code);
58+
},
59+
},
60+
};
61+
62+
define("py-game", {
63+
config: { packages: ["pygame-ce"] },
64+
configURL: new URL("./config.txt", location.href).href,
65+
interpreter: "pyodide",
66+
env: "py-game",
67+
hooks,
68+
});

core/tests/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
a:hover { opacity: 1; }
1515
</style>
1616
</head>
17-
<body><ul><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy-error.html">mpy-error<small>.html</small></a></li><li><a href="./javascript/mpy-no-error.html">mpy-no-error<small>.html</small></a></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./javascript/pyodide-cache/index.html">pyodide-cache</a></strong></li><li><strong><a href="./javascript/pyodide-lockfile/index.html">pyodide-lockfile</a></strong></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><span>workers</span></strong><ul><li><strong><a href="./javascript/workers/create_named/index.html">create_named</a></strong></li><li><strong><a href="./javascript/workers/mpy/index.html">mpy</a></strong></li><li><strong><a href="./javascript/workers/py/index.html">py</a></strong></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><strong><a href="./manual/donkey/index.html">donkey</a></strong></li><li><a href="./manual/emoji.html">emoji<small>.html</small></a></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><strong><a href="./manual/issue-2228/index.html">issue-2228</a></strong></li><li><strong><a href="./manual/issue-2246/index.html">issue-2246</a></strong></li><li><strong><a href="./manual/issue-7015/index.html">issue-7015</a></strong></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><strong><a href="./manual/no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./manual/piratical/index.html">piratical</a></strong></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><strong><a href="./manual/py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./manual/py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./manual/py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><strong><a href="./manual/service-worker/index.html">service-worker</a></strong></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li></ul></body>
17+
<body><ul><li><strong><span>javascript</span></strong><ul><li><a href="./javascript/async-listener.html">async-listener<small>.html</small></a></li><li><a href="./javascript/config-url.html">config-url<small>.html</small></a></li><li><a href="./javascript/config_type.html">config_type<small>.html</small></a></li><li><strong><a href="./javascript/fetch/index.html">fetch</a></strong></li><li><a href="./javascript/ffi.html">ffi<small>.html</small></a></li><li><a href="./javascript/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./javascript/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./javascript/js-storage.html">js-storage<small>.html</small></a></li><li><a href="./javascript/js_modules.html">js_modules<small>.html</small></a></li><li><strong><a href="./javascript/loader/index.html">loader</a></strong></li><li><a href="./javascript/mpy-error.html">mpy-error<small>.html</small></a></li><li><a href="./javascript/mpy-no-error.html">mpy-no-error<small>.html</small></a></li><li><a href="./javascript/mpy.html">mpy<small>.html</small></a></li><li><a href="./javascript/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./javascript/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./javascript/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./javascript/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./javascript/pyodide-cache/index.html">pyodide-cache</a></strong></li><li><strong><a href="./javascript/pyodide-lockfile/index.html">pyodide-lockfile</a></strong></li><li><a href="./javascript/storage.html">storage<small>.html</small></a></li><li><strong><span>workers</span></strong><ul><li><strong><a href="./javascript/workers/create_named/index.html">create_named</a></strong></li><li><strong><a href="./javascript/workers/mpy/index.html">mpy</a></strong></li><li><strong><a href="./javascript/workers/py/index.html">py</a></strong></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><strong><a href="./manual/donkey/index.html">donkey</a></strong></li><li><a href="./manual/emoji.html">emoji<small>.html</small></a></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><strong><a href="./manual/game/index.html">game</a></strong></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/interpreter.html">interpreter<small>.html</small></a></li><li><strong><a href="./manual/issue-2228/index.html">issue-2228</a></strong></li><li><strong><a href="./manual/issue-2246/index.html">issue-2246</a></strong></li><li><strong><a href="./manual/issue-7015/index.html">issue-7015</a></strong></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><strong><a href="./manual/no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./manual/piratical/index.html">piratical</a></strong></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py-editor-failure.html">py-editor-failure<small>.html</small></a></li><li><strong><a href="./manual/py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./manual/py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./manual/py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><strong><a href="./manual/service-worker/index.html">service-worker</a></strong></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/submit.html">submit<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./python/index.html">python</a></strong></li></ul></body>
1818
</html>

core/tests/manual/game/aliens.css

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* (c) https://github.com/ryanking13/pyodide-pygame-demo/blob/main/examples/aliens.html */
2+
body {
3+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
4+
margin: 0;
5+
padding: 20px;
6+
background-color: #f4f4f4;
7+
color: #333;
8+
}
9+
.demo {
10+
background-color: #fff;
11+
margin: 20px auto;
12+
max-width: 1000px;
13+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
14+
border-radius: 8px;
15+
overflow: hidden;
16+
}
17+
.demo-header {
18+
background-color: #007bff;
19+
color: #fff;
20+
padding: 15px 20px;
21+
font-size: 20px;
22+
}
23+
.demo-content {
24+
padding: 20px;
25+
}
26+
27+
#canvas {
28+
margin: 0 auto;
29+
display: block;
30+
}

0 commit comments

Comments
 (0)