mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 10:17:23 -05:00
Merge branch 'main' into poc_ui_blocks
This commit is contained in:
4
.github/workflows/publish-release.yml
vendored
4
.github/workflows/publish-release.yml
vendored
@@ -46,6 +46,10 @@ jobs:
|
||||
working-directory: .
|
||||
run: sed 's#_PATH_#https://pyscript.net/releases/${{ github.ref_name }}/#' ./public/index.html > ./pyscript.core/dist/index.html
|
||||
|
||||
- name: Generate release.tar from snapshot and put it in dist/
|
||||
working-directory: .
|
||||
run: tar -cvf ../release.tar * && mv ../release.tar .
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -142,6 +142,7 @@ coverage/
|
||||
test_results
|
||||
|
||||
# @pyscript/core npm artifacts
|
||||
pyscript.core/test-results/*
|
||||
pyscript.core/core.*
|
||||
pyscript.core/dist
|
||||
pyscript.core/dist.zip
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
||||
# Release Notes
|
||||
|
||||
## 2024.05.21
|
||||
|
||||
### Features
|
||||
|
||||
### Bug fixes
|
||||
|
||||
### Enhancements
|
||||
|
||||
- `py-editor` run buttons now display a spinner when disabled, which occurs when the editor is running code.
|
||||
|
||||
## 2023.05.01
|
||||
|
||||
### Features
|
||||
|
||||
@@ -59,9 +59,9 @@ If you would like to contribute to PyScript, but you aren't sure where to begin,
|
||||
|
||||
## Setting up your local environment and developing
|
||||
|
||||
If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://pyscript.github.io/docs/latest/development/setting-up-environment.html) will help you get started.
|
||||
If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://docs.pyscript.net/latest/contributing/#set-up-your-development-environment) will help you get started.
|
||||
|
||||
You can also read about PyScript's [development process](https://pyscript.github.io/docs/latest/development/developing.html) to learn how to contribute code to PyScript, how to run tests and what's the PR etiquette of the community!
|
||||
You can also read about PyScript's [development process](https://docs.pyscript.net/latest/developers/) to learn how to contribute code to PyScript, how to run tests and what's the PR etiquette of the community!
|
||||
|
||||
## License terms for contributions
|
||||
|
||||
|
||||
27
README.md
27
README.md
@@ -38,11 +38,11 @@ To try PyScript, import the appropriate pyscript files into the `<head>` tag of
|
||||
<head>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/releases/2023.11.2/core.css"
|
||||
href="https://pyscript.net/releases/2024.5.2/core.css"
|
||||
/>
|
||||
<script
|
||||
type="module"
|
||||
src="https://pyscript.net/releases/2023.11.2/core.js"
|
||||
src="https://pyscript.net/releases/2024.5.2/core.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -67,10 +67,29 @@ Check out the [official docs](https://docs.pyscript.net/) for more detailed docu
|
||||
|
||||
## How to Contribute
|
||||
|
||||
Read the [contributing guide](CONTRIBUTING.md) to learn about our development process, reporting bugs and improvements, creating issues and asking questions.
|
||||
Read the [contributing guide](https://docs.pyscript.net/latest/contributing/) to learn about our development process, reporting bugs and improvements, creating issues and asking questions.
|
||||
|
||||
Check out the [developing process](https://pyscript.github.io/docs/latest/contributing) documentation for more information on how to setup your development environment.
|
||||
Check out the [developing process](https://docs.pyscript.net/latest/developers/) documentation for more information on how to setup your development environment.
|
||||
|
||||
## Governance
|
||||
|
||||
The [PyScript organization governance](https://github.com/pyscript/governance) is documented in a separate repository.
|
||||
|
||||
## Release
|
||||
|
||||
To cut a new release of PyScript simply
|
||||
[add a new release](https://github.com/pyscript/pyscript/releases) while
|
||||
remembering to write a comprehensive changelog. A [GitHub action](https://github.com/pyscript/pyscript/blob/main/.github/workflows/publish-release.yml)
|
||||
will kick in and ensure the release is described and deployed to a URL with the
|
||||
pattern: https://pyscript.net/releases/YYYY.M.v/ (year/month/version - as per
|
||||
our [CalVer](https://calver.org/) versioning scheme).
|
||||
|
||||
Then, the following three separate repositories need updating:
|
||||
|
||||
- [Documentation](https://github.com/pyscript/docs) - Change the `version.json`
|
||||
file in the root of the directory and then `node version-update.js`.
|
||||
- [Homepage](https://github.com/pyscript/pyscript.net) - Ensure the version
|
||||
referenced in `index.html` is the latest version.
|
||||
- [PSDC](https://pyscript.com) - Use discord or Anaconda Slack (if you work at
|
||||
Anaconda) to let the PSDC team know there's a new version, so they can update
|
||||
their project templates.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
},
|
||||
extends: "eslint:recommended",
|
||||
overrides: [
|
||||
{
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
files: [".eslintrc.{js,cjs}"],
|
||||
parserOptions: {
|
||||
sourceType: "script",
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
ignorePatterns: ["3rd-party"],
|
||||
rules: {
|
||||
"no-implicit-globals": ["error"],
|
||||
},
|
||||
};
|
||||
@@ -1,9 +1,11 @@
|
||||
.eslintrc.cjs
|
||||
eslint.config.mjs
|
||||
.pytest_cache/
|
||||
node_modules/
|
||||
rollup/
|
||||
test/
|
||||
tests/
|
||||
test-results/
|
||||
src/stdlib/_pyscript
|
||||
src/stdlib/pyscript.py
|
||||
package-lock.json
|
||||
|
||||
22
pyscript.core/eslint.config.mjs
Normal file
22
pyscript.core/eslint.config.mjs
Normal file
@@ -0,0 +1,22 @@
|
||||
import globals from "globals";
|
||||
import js from "@eslint/js";
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
{
|
||||
ignores: ["**/3rd-party/"],
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2021,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"no-implicit-globals": ["error"],
|
||||
},
|
||||
},
|
||||
];
|
||||
1028
pyscript.core/package-lock.json
generated
1028
pyscript.core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.4.31",
|
||||
"version": "0.4.41",
|
||||
"type": "module",
|
||||
"description": "PyScript",
|
||||
"module": "./index.js",
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"server": "npx static-handler --coi .",
|
||||
"build": "export ESLINT_USE_FLAT_CONFIG=false; npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && eslint src/ && npm run ts && npm run test:mpy",
|
||||
"build": "export ESLINT_USE_FLAT_CONFIG=true;npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && eslint src/ && npm run ts && npm run test:mpy",
|
||||
"build:core": "rm -rf dist && rollup --config rollup/core.config.js && cp src/3rd-party/*.css dist/",
|
||||
"build:plugins": "node rollup/plugins.cjs",
|
||||
"build:stdlib": "node rollup/stdlib.cjs",
|
||||
@@ -43,7 +43,7 @@
|
||||
"dependencies": {
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"basic-devtools": "^0.1.6",
|
||||
"polyscript": "^0.12.8",
|
||||
"polyscript": "^0.12.14",
|
||||
"sticky-module": "^0.1.1",
|
||||
"to-json-callback": "^0.1.1",
|
||||
"type-checked-collections": "^0.1.7"
|
||||
@@ -54,18 +54,18 @@
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/view": "^6.26.3",
|
||||
"@playwright/test": "^1.44.0",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@playwright/test": "^1.44.1",
|
||||
"@rollup/plugin-commonjs": "^25.0.8",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@webreflection/toml-j0.4": "^1.1.3",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"bun": "^1.1.7",
|
||||
"bun": "^1.1.12",
|
||||
"chokidar": "^3.6.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"eslint": "^9.2.0",
|
||||
"rollup": "^4.17.2",
|
||||
"eslint": "^9.4.0",
|
||||
"rollup": "^4.18.0",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"static-handler": "^0.4.3",
|
||||
|
||||
@@ -42,3 +42,34 @@ mpy-config {
|
||||
.mpy-editor-run-button:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes spinner {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.py-editor-run-button:disabled > *,
|
||||
.mpy-editor-run-button:disabled > * {
|
||||
display: none; /* hide all the child elements of the run button when it is disabled */
|
||||
}
|
||||
.py-editor-run-button:disabled,
|
||||
.mpy-editor-run-button:disabled {
|
||||
border-width: 0;
|
||||
}
|
||||
.py-editor-run-button:disabled::before,
|
||||
.mpy-editor-run-button:disabled::before {
|
||||
content: "";
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -23px; /* hardcoded value to center the spinner on the run button */
|
||||
margin-left: -26px; /* hardcoded value to center the spinner on the run button */
|
||||
border-radius: 50%;
|
||||
border: 2px solid #aaa;
|
||||
border-top-color: #000;
|
||||
background-color: #fff;
|
||||
animation: spinner 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,17 @@ import sync from "./sync.js";
|
||||
import bootstrapNodeAndPlugins from "./plugins-helper.js";
|
||||
import { ErrorCode } from "./exceptions.js";
|
||||
import { robustFetch as fetch, getText } from "./fetch.js";
|
||||
import { hooks, main, worker, codeFor, createFunction } from "./hooks.js";
|
||||
import {
|
||||
hooks,
|
||||
main,
|
||||
worker,
|
||||
codeFor,
|
||||
createFunction,
|
||||
inputFailure,
|
||||
} from "./hooks.js";
|
||||
|
||||
import { stdlib, optional } from "./stdlib.js";
|
||||
export { stdlib, optional };
|
||||
export { stdlib, optional, inputFailure };
|
||||
|
||||
// generic helper to disambiguate between custom element and script
|
||||
const isScript = ({ tagName }) => tagName === "SCRIPT";
|
||||
|
||||
@@ -46,7 +46,7 @@ export const createFunction = (self, name) => {
|
||||
const SetFunction = typedSet({ typeof: "function" });
|
||||
const SetString = typedSet({ typeof: "string" });
|
||||
|
||||
const inputFailure = `
|
||||
export const inputFailure = `
|
||||
import builtins
|
||||
def input(prompt=""):
|
||||
raise Exception("\\n ".join([
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// PyScript py-editor plugin
|
||||
import { Hook, XWorker, dedent } from "polyscript/exports";
|
||||
import { Hook, XWorker, dedent, defineProperties } from "polyscript/exports";
|
||||
import { TYPES, offline_interpreter, stdlib } from "../core.js";
|
||||
|
||||
const RUN_BUTTON = `<svg style="height:20px;width:20px;vertical-align:-.125em;transform-origin:center;overflow:visible;color:green" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>`;
|
||||
@@ -43,6 +43,8 @@ async function execute({ currentTarget }) {
|
||||
: JSON;
|
||||
details.config = parse(await fetch(config).then((r) => r.text()));
|
||||
details.version = offline_interpreter(details.config);
|
||||
} else {
|
||||
details.config = {};
|
||||
}
|
||||
|
||||
const xworker = XWorker.call(new Hook(null, hooks), srcLink, details);
|
||||
@@ -58,7 +60,7 @@ async function execute({ currentTarget }) {
|
||||
|
||||
// wait for the env then set the target div
|
||||
// before executing the current code
|
||||
envs.get(env).then((xworker) => {
|
||||
return envs.get(env).then((xworker) => {
|
||||
xworker.onerror = ({ error }) => {
|
||||
if (hasRunButton) {
|
||||
outDiv.innerHTML += `<span style='color:red'>${
|
||||
@@ -148,7 +150,7 @@ const init = async (script, type, interpreter) => {
|
||||
import(/* webpackIgnore: true */ "../3rd-party/codemirror_commands.js"),
|
||||
]);
|
||||
|
||||
const isSetup = script.hasAttribute("setup");
|
||||
let isSetup = script.hasAttribute("setup");
|
||||
const hasConfig = script.hasAttribute("config");
|
||||
const env = `${interpreter}-${script.getAttribute("env") || getID(type)}`;
|
||||
|
||||
@@ -162,7 +164,7 @@ const init = async (script, type, interpreter) => {
|
||||
|
||||
configs.set(env, hasConfig);
|
||||
|
||||
const source = script.src
|
||||
let source = script.src
|
||||
? await fetch(script.src).then((b) => b.text())
|
||||
: script.textContent;
|
||||
const context = {
|
||||
@@ -179,14 +181,59 @@ const init = async (script, type, interpreter) => {
|
||||
},
|
||||
};
|
||||
|
||||
let target;
|
||||
defineProperties(script, {
|
||||
target: { get: () => target },
|
||||
code: {
|
||||
get: () => context.pySrc,
|
||||
set: (insert) => {
|
||||
if (isSetup) return;
|
||||
editor.update([
|
||||
editor.state.update({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.state.doc.length,
|
||||
insert,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
},
|
||||
},
|
||||
process: {
|
||||
/**
|
||||
* Simulate a setup node overriding the source to evaluate.
|
||||
* @param {string} code the Python code to evaluate.
|
||||
* @returns {Promise<...>} fulfill once code has been evaluated.
|
||||
*/
|
||||
value(code) {
|
||||
const wasSetup = isSetup;
|
||||
const wasSource = source;
|
||||
isSetup = true;
|
||||
source = code;
|
||||
const restore = () => {
|
||||
isSetup = wasSetup;
|
||||
source = wasSource;
|
||||
};
|
||||
return execute
|
||||
.call(context, { currentTarget: null })
|
||||
.then(restore, restore);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const notify = () => {
|
||||
const event = new Event(`${type}-editor`, { bubbles: true });
|
||||
script.dispatchEvent(event);
|
||||
};
|
||||
|
||||
if (isSetup) {
|
||||
execute.call(context, { currentTarget: null });
|
||||
await execute.call(context, { currentTarget: null });
|
||||
notify();
|
||||
return;
|
||||
}
|
||||
|
||||
const selector = script.getAttribute("target");
|
||||
|
||||
let target;
|
||||
if (selector) {
|
||||
target =
|
||||
document.getElementById(selector) ||
|
||||
@@ -236,6 +283,7 @@ const init = async (script, type, interpreter) => {
|
||||
});
|
||||
|
||||
editor.focus();
|
||||
notify();
|
||||
};
|
||||
|
||||
// avoid too greedy MutationObserver operations at distance
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// PyScript py-terminal plugin
|
||||
import { TYPES, hooks } from "../core.js";
|
||||
import { TYPES } from "../core.js";
|
||||
import { notify } from "./error.js";
|
||||
import { customObserver, defineProperties } from "polyscript/exports";
|
||||
import { customObserver } from "polyscript/exports";
|
||||
|
||||
// will contain all valid selectors
|
||||
const SELECTORS = [];
|
||||
|
||||
// avoid processing same elements twice
|
||||
const processed = new WeakSet();
|
||||
|
||||
// show the error on main and
|
||||
// stops the module from keep executing
|
||||
const notifyAndThrow = (message) => {
|
||||
@@ -15,265 +18,10 @@ const notifyAndThrow = (message) => {
|
||||
|
||||
const onceOnMain = ({ attributes: { worker } }) => !worker;
|
||||
|
||||
const bootstrapped = new WeakSet();
|
||||
|
||||
let addStyle = true;
|
||||
|
||||
// this callback will be serialized as string and it never needs
|
||||
// to be invoked multiple times. Each xworker here is bootstrapped
|
||||
// only once thanks to the `sync.is_pyterminal()` check.
|
||||
const workerReady = ({ interpreter, io, run, type }, { sync }) => {
|
||||
if (!sync.is_pyterminal()) return;
|
||||
|
||||
// in workers it's always safe to grab the polyscript currentScript
|
||||
// the ugly `_` dance is due MicroPython not able to import via:
|
||||
// `from polyscript.currentScript import terminal as __terminal__`
|
||||
run(
|
||||
"from polyscript import currentScript as _; __terminal__ = _.terminal; del _",
|
||||
);
|
||||
|
||||
let data = "";
|
||||
const { pyterminal_read, pyterminal_write } = sync;
|
||||
const decoder = new TextDecoder();
|
||||
const generic = {
|
||||
isatty: false,
|
||||
write(buffer) {
|
||||
data = decoder.decode(buffer);
|
||||
pyterminal_write(data);
|
||||
return buffer.length;
|
||||
},
|
||||
};
|
||||
|
||||
// This part works already in both Pyodide and MicroPython
|
||||
io.stderr = (error) => {
|
||||
pyterminal_write(String(error.message || error));
|
||||
};
|
||||
|
||||
// MicroPython has no code or code.interact()
|
||||
// This part patches it in a way that simulates
|
||||
// the code.interact() module in Pyodide.
|
||||
if (type === "mpy") {
|
||||
// monkey patch global input otherwise broken in MicroPython
|
||||
interpreter.registerJsModule("_pyscript_input", {
|
||||
input: pyterminal_read,
|
||||
});
|
||||
run("from _pyscript_input import input");
|
||||
|
||||
// this is needed to avoid truncated unicode in MicroPython
|
||||
// the reason is that `linebuffer` false just send one byte
|
||||
// per time and readline here doesn't like it much.
|
||||
// MicroPython also has issues with code-points and
|
||||
// replProcessChar(byte) but that function accepts only
|
||||
// one byte per time so ... we have an issue!
|
||||
// @see https://github.com/pyscript/pyscript/pull/2018
|
||||
// @see https://github.com/WebReflection/buffer-points
|
||||
const bufferPoints = (stdio) => {
|
||||
const bytes = [];
|
||||
let needed = 0;
|
||||
return (buffer) => {
|
||||
let written = 0;
|
||||
for (const byte of buffer) {
|
||||
bytes.push(byte);
|
||||
// @see https://encoding.spec.whatwg.org/#utf-8-bytes-needed
|
||||
if (needed) needed--;
|
||||
else if (0xc2 <= byte && byte <= 0xdf) needed = 1;
|
||||
else if (0xe0 <= byte && byte <= 0xef) needed = 2;
|
||||
else if (0xf0 <= byte && byte <= 0xf4) needed = 3;
|
||||
if (!needed) {
|
||||
written += bytes.length;
|
||||
stdio(new Uint8Array(bytes.splice(0)));
|
||||
}
|
||||
}
|
||||
return written;
|
||||
};
|
||||
};
|
||||
|
||||
io.stdout = bufferPoints(generic.write);
|
||||
|
||||
// tiny shim of the code module with only interact
|
||||
// to bootstrap a REPL like environment
|
||||
interpreter.registerJsModule("code", {
|
||||
interact() {
|
||||
let input = "";
|
||||
let length = 1;
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const acc = [];
|
||||
const handlePoints = bufferPoints((buffer) => {
|
||||
acc.push(...buffer);
|
||||
pyterminal_write(decoder.decode(buffer));
|
||||
});
|
||||
|
||||
// avoid duplicating the output produced by the input
|
||||
io.stdout = (buffer) =>
|
||||
length++ > input.length ? handlePoints(buffer) : 0;
|
||||
|
||||
interpreter.replInit();
|
||||
|
||||
// loop forever waiting for user inputs
|
||||
(function repl() {
|
||||
const out = decoder.decode(new Uint8Array(acc.splice(0)));
|
||||
// print in current line only the last line produced by the REPL
|
||||
const data = `${pyterminal_read(out.split("\n").at(-1))}\r`;
|
||||
length = 0;
|
||||
input = encoder.encode(data);
|
||||
for (const c of input) interpreter.replProcessChar(c);
|
||||
repl();
|
||||
})();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
interpreter.setStdout(generic);
|
||||
interpreter.setStderr(generic);
|
||||
interpreter.setStdin({
|
||||
isatty: false,
|
||||
stdin: () => pyterminal_read(data),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const pyTerminal = async (element) => {
|
||||
// lazy load these only when a valid terminal is found
|
||||
const [{ Terminal }, { Readline }, { FitAddon }, { WebLinksAddon }] =
|
||||
await Promise.all([
|
||||
import(/* webpackIgnore: true */ "../3rd-party/xterm.js"),
|
||||
import(/* webpackIgnore: true */ "../3rd-party/xterm-readline.js"),
|
||||
import(/* webpackIgnore: true */ "../3rd-party/xterm_addon-fit.js"),
|
||||
import(
|
||||
/* webpackIgnore: true */ "../3rd-party/xterm_addon-web-links.js"
|
||||
),
|
||||
]);
|
||||
|
||||
const readline = new Readline();
|
||||
|
||||
// common main thread initialization for both worker
|
||||
// or main case, bootstrapping the terminal on its target
|
||||
const init = (options) => {
|
||||
let target = element;
|
||||
const selector = element.getAttribute("target");
|
||||
if (selector) {
|
||||
target =
|
||||
document.getElementById(selector) ||
|
||||
document.querySelector(selector);
|
||||
if (!target) throw new Error(`Unknown target ${selector}`);
|
||||
} else {
|
||||
target = document.createElement("py-terminal");
|
||||
target.style.display = "block";
|
||||
element.after(target);
|
||||
}
|
||||
const terminal = new Terminal({
|
||||
theme: {
|
||||
background: "#191A19",
|
||||
foreground: "#F5F2E7",
|
||||
},
|
||||
...options,
|
||||
});
|
||||
const fitAddon = new FitAddon();
|
||||
terminal.loadAddon(fitAddon);
|
||||
terminal.loadAddon(readline);
|
||||
terminal.loadAddon(new WebLinksAddon());
|
||||
terminal.open(target);
|
||||
fitAddon.fit();
|
||||
terminal.focus();
|
||||
defineProperties(element, {
|
||||
terminal: { value: terminal },
|
||||
process: {
|
||||
value: async (code) => {
|
||||
// this loop is the only way I could find to actually simulate
|
||||
// the user input char after char in a way that works in both
|
||||
// MicroPython and Pyodide
|
||||
for (const line of code.split(/(?:\r|\n|\r\n)/)) {
|
||||
terminal.paste(`${line}\n`);
|
||||
do {
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, 0),
|
||||
);
|
||||
} while (!readline.activeRead?.resolve);
|
||||
readline.activeRead.resolve(line);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
return terminal;
|
||||
};
|
||||
|
||||
// branch logic for the worker
|
||||
if (element.hasAttribute("worker")) {
|
||||
// add a hook on the main thread to setup all sync helpers
|
||||
// also bootstrapping the XTerm target on main *BUT* ...
|
||||
hooks.main.onWorker.add(function worker(_, xworker) {
|
||||
// ... as multiple workers will add multiple callbacks
|
||||
// be sure no xworker is ever initialized twice!
|
||||
if (bootstrapped.has(xworker)) return;
|
||||
bootstrapped.add(xworker);
|
||||
|
||||
// still cleanup this callback for future scripts/workers
|
||||
hooks.main.onWorker.delete(worker);
|
||||
|
||||
init({
|
||||
disableStdin: false,
|
||||
cursorBlink: true,
|
||||
cursorStyle: "block",
|
||||
});
|
||||
|
||||
xworker.sync.is_pyterminal = () => true;
|
||||
xworker.sync.pyterminal_read = readline.read.bind(readline);
|
||||
xworker.sync.pyterminal_write = readline.write.bind(readline);
|
||||
});
|
||||
|
||||
// setup remote thread JS/Python code for whenever the
|
||||
// worker is ready to become a terminal
|
||||
hooks.worker.onReady.add(workerReady);
|
||||
} else {
|
||||
// in the main case, just bootstrap XTerm without
|
||||
// allowing any input as that's not possible / awkward
|
||||
hooks.main.onReady.add(function main({ interpreter, io, run, type }) {
|
||||
console.warn("py-terminal is read only on main thread");
|
||||
hooks.main.onReady.delete(main);
|
||||
|
||||
// on main, it's easy to trash and clean the current terminal
|
||||
globalThis.__py_terminal__ = init({
|
||||
disableStdin: true,
|
||||
cursorBlink: false,
|
||||
cursorStyle: "underline",
|
||||
});
|
||||
run("from js import __py_terminal__ as __terminal__");
|
||||
delete globalThis.__py_terminal__;
|
||||
|
||||
io.stderr = (error) => {
|
||||
readline.write(String(error.message || error));
|
||||
};
|
||||
|
||||
if (type === "mpy") {
|
||||
interpreter.setStdin = Object; // as no-op
|
||||
interpreter.setStderr = Object; // as no-op
|
||||
interpreter.setStdout = ({ write }) => {
|
||||
io.stdout = write;
|
||||
};
|
||||
}
|
||||
|
||||
let data = "";
|
||||
const decoder = new TextDecoder();
|
||||
const generic = {
|
||||
isatty: false,
|
||||
write(buffer) {
|
||||
data = decoder.decode(buffer);
|
||||
readline.write(data);
|
||||
return buffer.length;
|
||||
},
|
||||
};
|
||||
interpreter.setStdout(generic);
|
||||
interpreter.setStderr(generic);
|
||||
interpreter.setStdin({
|
||||
isatty: false,
|
||||
stdin: () => readline.read(data),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
for (const key of TYPES.keys()) {
|
||||
const selector = `script[type="${key}"][terminal],${key}-script[terminal]`;
|
||||
for (const type of TYPES.keys()) {
|
||||
const selector = `script[type="${type}"][terminal],${type}-script[terminal]`;
|
||||
SELECTORS.push(selector);
|
||||
customObserver.set(selector, async (element) => {
|
||||
// we currently support only one terminal on main as in "classic"
|
||||
@@ -292,6 +40,21 @@ for (const key of TYPES.keys()) {
|
||||
);
|
||||
}
|
||||
|
||||
await pyTerminal(element);
|
||||
if (processed.has(element)) return;
|
||||
processed.add(element);
|
||||
|
||||
const bootstrap = (module) => module.default(element);
|
||||
|
||||
// we can't be smart with template literals for the dynamic import
|
||||
// or bundlers are incapable of producing multiple files around
|
||||
if (type === "mpy") {
|
||||
await import(/* webpackIgnore: true */ "./py-terminal/mpy.js").then(
|
||||
bootstrap,
|
||||
);
|
||||
} else {
|
||||
await import(/* webpackIgnore: true */ "./py-terminal/py.js").then(
|
||||
bootstrap,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
239
pyscript.core/src/plugins/py-terminal/mpy.js
Normal file
239
pyscript.core/src/plugins/py-terminal/mpy.js
Normal file
@@ -0,0 +1,239 @@
|
||||
// PyScript pyodide terminal plugin
|
||||
import { hooks, inputFailure } from "../../core.js";
|
||||
import { defineProperties } from "polyscript/exports";
|
||||
|
||||
const bootstrapped = new WeakSet();
|
||||
|
||||
// this callback will be serialized as string and it never needs
|
||||
// to be invoked multiple times. Each xworker here is bootstrapped
|
||||
// only once thanks to the `sync.is_pyterminal()` check.
|
||||
const workerReady = ({ interpreter, io, run, type }, { sync }) => {
|
||||
if (type !== "mpy" || !sync.is_pyterminal()) return;
|
||||
|
||||
const { pyterminal_ready, pyterminal_read, pyterminal_write } = sync;
|
||||
|
||||
interpreter.registerJsModule("_pyscript_input", {
|
||||
input: pyterminal_read,
|
||||
});
|
||||
|
||||
run(
|
||||
[
|
||||
"from _pyscript_input import input",
|
||||
"from polyscript import currentScript as _",
|
||||
"__terminal__ = _.terminal",
|
||||
"del _",
|
||||
].join(";"),
|
||||
);
|
||||
|
||||
const missingReturn = new Uint8Array([13]);
|
||||
io.stdout = (buffer) => {
|
||||
if (buffer[0] === 10) pyterminal_write(missingReturn);
|
||||
pyterminal_write(buffer);
|
||||
};
|
||||
io.stderr = (error) => {
|
||||
pyterminal_write(String(error.message || error));
|
||||
};
|
||||
|
||||
// tiny shim of the code module with only interact
|
||||
// to bootstrap a REPL like environment
|
||||
interpreter.registerJsModule("code", {
|
||||
interact() {
|
||||
const encoder = new TextEncoderStream();
|
||||
encoder.readable.pipeTo(
|
||||
new WritableStream({
|
||||
write(buffer) {
|
||||
for (const c of buffer) interpreter.replProcessChar(c);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const writer = encoder.writable.getWriter();
|
||||
sync.pyterminal_stream_write = (buffer) => writer.write(buffer);
|
||||
pyterminal_ready();
|
||||
|
||||
interpreter.replInit();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default async (element) => {
|
||||
// lazy load these only when a valid terminal is found
|
||||
const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = await Promise.all([
|
||||
import(/* webpackIgnore: true */ "../../3rd-party/xterm.js"),
|
||||
import(/* webpackIgnore: true */ "../../3rd-party/xterm_addon-fit.js"),
|
||||
import(
|
||||
/* webpackIgnore: true */ "../../3rd-party/xterm_addon-web-links.js"
|
||||
),
|
||||
]);
|
||||
|
||||
const terminalOptions = {
|
||||
disableStdin: false,
|
||||
cursorBlink: true,
|
||||
cursorStyle: "block",
|
||||
};
|
||||
|
||||
let stream;
|
||||
|
||||
// common main thread initialization for both worker
|
||||
// or main case, bootstrapping the terminal on its target
|
||||
const init = () => {
|
||||
let target = element;
|
||||
const selector = element.getAttribute("target");
|
||||
if (selector) {
|
||||
target =
|
||||
document.getElementById(selector) ||
|
||||
document.querySelector(selector);
|
||||
if (!target) throw new Error(`Unknown target ${selector}`);
|
||||
} else {
|
||||
target = document.createElement("py-terminal");
|
||||
target.style.display = "block";
|
||||
element.after(target);
|
||||
}
|
||||
const terminal = new Terminal({
|
||||
theme: {
|
||||
background: "#191A19",
|
||||
foreground: "#F5F2E7",
|
||||
},
|
||||
...terminalOptions,
|
||||
});
|
||||
const fitAddon = new FitAddon();
|
||||
terminal.loadAddon(fitAddon);
|
||||
terminal.loadAddon(new WebLinksAddon());
|
||||
terminal.open(target);
|
||||
fitAddon.fit();
|
||||
terminal.focus();
|
||||
defineProperties(element, {
|
||||
terminal: { value: terminal },
|
||||
process: {
|
||||
value: async (code) => {
|
||||
for (const line of code.split(/(?:\r\n|\r|\n)/)) {
|
||||
await stream.write(`${line}\r`);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
return terminal;
|
||||
};
|
||||
|
||||
// branch logic for the worker
|
||||
if (element.hasAttribute("worker")) {
|
||||
// add a hook on the main thread to setup all sync helpers
|
||||
// also bootstrapping the XTerm target on main *BUT* ...
|
||||
hooks.main.onWorker.add(function worker(_, xworker) {
|
||||
// ... as multiple workers will add multiple callbacks
|
||||
// be sure no xworker is ever initialized twice!
|
||||
if (bootstrapped.has(xworker)) return;
|
||||
bootstrapped.add(xworker);
|
||||
|
||||
// still cleanup this callback for future scripts/workers
|
||||
hooks.main.onWorker.delete(worker);
|
||||
|
||||
const terminal = init();
|
||||
|
||||
const { sync } = xworker;
|
||||
|
||||
// handle the read mode on input
|
||||
let promisedChunks = null;
|
||||
let readChunks = "";
|
||||
|
||||
sync.is_pyterminal = () => true;
|
||||
|
||||
// put the terminal in a read-only state
|
||||
// frees the worker on \r
|
||||
sync.pyterminal_read = (buffer) => {
|
||||
terminal.write(buffer);
|
||||
promisedChunks = Promise.withResolvers();
|
||||
return promisedChunks.promise;
|
||||
};
|
||||
|
||||
// write if not reading input
|
||||
sync.pyterminal_write = (buffer) => {
|
||||
if (!promisedChunks) terminal.write(buffer);
|
||||
};
|
||||
|
||||
// add the onData terminal listener which forwards to the worker
|
||||
// everything typed in a queued char-by-char way
|
||||
sync.pyterminal_ready = () => {
|
||||
let queue = Promise.resolve();
|
||||
stream = {
|
||||
write: (buffer) =>
|
||||
(queue = queue.then(() =>
|
||||
sync.pyterminal_stream_write(buffer),
|
||||
)),
|
||||
};
|
||||
terminal.onData((buffer) => {
|
||||
if (promisedChunks) {
|
||||
readChunks += buffer;
|
||||
terminal.write(buffer);
|
||||
if (readChunks.endsWith("\r")) {
|
||||
terminal.write("\n");
|
||||
promisedChunks.resolve(readChunks.slice(0, -1));
|
||||
promisedChunks = null;
|
||||
readChunks = "";
|
||||
}
|
||||
} else {
|
||||
stream.write(buffer);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
// setup remote thread JS/Python code for whenever the
|
||||
// worker is ready to become a terminal
|
||||
hooks.worker.onReady.add(workerReady);
|
||||
} else {
|
||||
// ⚠️ In an ideal world the inputFailure should never be used on main.
|
||||
// However, Pyodide still can't compete with MicroPython REPL mode
|
||||
// so while it's OK to keep that entry on main as default, we need
|
||||
// to remove it ASAP from `mpy` use cases, otherwise MicroPython would
|
||||
// also throw whenever an `input(...)` is required / digited.
|
||||
hooks.main.codeBeforeRun.delete(inputFailure);
|
||||
|
||||
// in the main case, just bootstrap XTerm without
|
||||
// allowing any input as that's not possible / awkward
|
||||
hooks.main.onReady.add(function main({ interpreter, io, run, type }) {
|
||||
if (type !== "mpy") return;
|
||||
|
||||
hooks.main.onReady.delete(main);
|
||||
|
||||
const terminal = init();
|
||||
|
||||
const missingReturn = new Uint8Array([13]);
|
||||
io.stdout = (buffer) => {
|
||||
if (buffer[0] === 10) terminal.write(missingReturn);
|
||||
terminal.write(buffer);
|
||||
};
|
||||
|
||||
// expose the __terminal__ one-off reference
|
||||
globalThis.__py_terminal__ = terminal;
|
||||
run(
|
||||
[
|
||||
"from js import prompt as input",
|
||||
"from js import __py_terminal__ as __terminal__",
|
||||
].join(";"),
|
||||
);
|
||||
delete globalThis.__py_terminal__;
|
||||
|
||||
// NOTE: this is NOT the same as the one within
|
||||
// the onWorkerReady callback!
|
||||
interpreter.registerJsModule("code", {
|
||||
interact() {
|
||||
const encoder = new TextEncoderStream();
|
||||
encoder.readable.pipeTo(
|
||||
new WritableStream({
|
||||
write(buffer) {
|
||||
for (const c of buffer)
|
||||
interpreter.replProcessChar(c);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
stream = encoder.writable.getWriter();
|
||||
terminal.onData((buffer) => stream.write(buffer));
|
||||
|
||||
interpreter.replInit();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
179
pyscript.core/src/plugins/py-terminal/py.js
Normal file
179
pyscript.core/src/plugins/py-terminal/py.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// PyScript py-terminal plugin
|
||||
import { hooks } from "../../core.js";
|
||||
import { defineProperties } from "polyscript/exports";
|
||||
|
||||
const bootstrapped = new WeakSet();
|
||||
|
||||
// this callback will be serialized as string and it never needs
|
||||
// to be invoked multiple times. Each xworker here is bootstrapped
|
||||
// only once thanks to the `sync.is_pyterminal()` check.
|
||||
const workerReady = ({ interpreter, io, run, type }, { sync }) => {
|
||||
if (type !== "py" || !sync.is_pyterminal()) return;
|
||||
|
||||
run(
|
||||
[
|
||||
"from polyscript import currentScript as _",
|
||||
"__terminal__ = _.terminal",
|
||||
"del _",
|
||||
].join(";"),
|
||||
);
|
||||
|
||||
let data = "";
|
||||
const { pyterminal_read, pyterminal_write } = sync;
|
||||
const decoder = new TextDecoder();
|
||||
const generic = {
|
||||
isatty: false,
|
||||
write(buffer) {
|
||||
data = decoder.decode(buffer);
|
||||
pyterminal_write(data);
|
||||
return buffer.length;
|
||||
},
|
||||
};
|
||||
|
||||
io.stderr = (error) => {
|
||||
pyterminal_write(String(error.message || error));
|
||||
};
|
||||
|
||||
interpreter.setStdout(generic);
|
||||
interpreter.setStderr(generic);
|
||||
interpreter.setStdin({
|
||||
isatty: false,
|
||||
stdin: () => pyterminal_read(data),
|
||||
});
|
||||
};
|
||||
|
||||
export default async (element) => {
|
||||
// lazy load these only when a valid terminal is found
|
||||
const [{ Terminal }, { Readline }, { FitAddon }, { WebLinksAddon }] =
|
||||
await Promise.all([
|
||||
import(/* webpackIgnore: true */ "../../3rd-party/xterm.js"),
|
||||
import(
|
||||
/* webpackIgnore: true */ "../../3rd-party/xterm-readline.js"
|
||||
),
|
||||
import(
|
||||
/* webpackIgnore: true */ "../../3rd-party/xterm_addon-fit.js"
|
||||
),
|
||||
import(
|
||||
/* webpackIgnore: true */ "../../3rd-party/xterm_addon-web-links.js"
|
||||
),
|
||||
]);
|
||||
|
||||
const readline = new Readline();
|
||||
|
||||
// common main thread initialization for both worker
|
||||
// or main case, bootstrapping the terminal on its target
|
||||
const init = (options) => {
|
||||
let target = element;
|
||||
const selector = element.getAttribute("target");
|
||||
if (selector) {
|
||||
target =
|
||||
document.getElementById(selector) ||
|
||||
document.querySelector(selector);
|
||||
if (!target) throw new Error(`Unknown target ${selector}`);
|
||||
} else {
|
||||
target = document.createElement("py-terminal");
|
||||
target.style.display = "block";
|
||||
element.after(target);
|
||||
}
|
||||
const terminal = new Terminal({
|
||||
theme: {
|
||||
background: "#191A19",
|
||||
foreground: "#F5F2E7",
|
||||
},
|
||||
...options,
|
||||
});
|
||||
const fitAddon = new FitAddon();
|
||||
terminal.loadAddon(fitAddon);
|
||||
terminal.loadAddon(readline);
|
||||
terminal.loadAddon(new WebLinksAddon());
|
||||
terminal.open(target);
|
||||
fitAddon.fit();
|
||||
terminal.focus();
|
||||
defineProperties(element, {
|
||||
terminal: { value: terminal },
|
||||
process: {
|
||||
value: async (code) => {
|
||||
for (const line of code.split(/(?:\r\n|\r|\n)/)) {
|
||||
terminal.paste(`${line}`);
|
||||
terminal.write("\r\n");
|
||||
do {
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, 0),
|
||||
);
|
||||
} while (!readline.activeRead?.resolve);
|
||||
readline.activeRead.resolve(line);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
return terminal;
|
||||
};
|
||||
|
||||
// branch logic for the worker
|
||||
if (element.hasAttribute("worker")) {
|
||||
// add a hook on the main thread to setup all sync helpers
|
||||
// also bootstrapping the XTerm target on main *BUT* ...
|
||||
hooks.main.onWorker.add(function worker(_, xworker) {
|
||||
// ... as multiple workers will add multiple callbacks
|
||||
// be sure no xworker is ever initialized twice!
|
||||
if (bootstrapped.has(xworker)) return;
|
||||
bootstrapped.add(xworker);
|
||||
|
||||
// still cleanup this callback for future scripts/workers
|
||||
hooks.main.onWorker.delete(worker);
|
||||
|
||||
init({
|
||||
disableStdin: false,
|
||||
cursorBlink: true,
|
||||
cursorStyle: "block",
|
||||
});
|
||||
|
||||
xworker.sync.is_pyterminal = () => true;
|
||||
xworker.sync.pyterminal_read = readline.read.bind(readline);
|
||||
xworker.sync.pyterminal_write = readline.write.bind(readline);
|
||||
});
|
||||
|
||||
// setup remote thread JS/Python code for whenever the
|
||||
// worker is ready to become a terminal
|
||||
hooks.worker.onReady.add(workerReady);
|
||||
} else {
|
||||
// in the main case, just bootstrap XTerm without
|
||||
// allowing any input as that's not possible / awkward
|
||||
hooks.main.onReady.add(function main({ interpreter, io, run, type }) {
|
||||
if (type !== "py") return;
|
||||
|
||||
console.warn("py-terminal is read only on main thread");
|
||||
hooks.main.onReady.delete(main);
|
||||
|
||||
// on main, it's easy to trash and clean the current terminal
|
||||
globalThis.__py_terminal__ = init({
|
||||
disableStdin: true,
|
||||
cursorBlink: false,
|
||||
cursorStyle: "underline",
|
||||
});
|
||||
run("from js import __py_terminal__ as __terminal__");
|
||||
delete globalThis.__py_terminal__;
|
||||
|
||||
io.stderr = (error) => {
|
||||
readline.write(String(error.message || error));
|
||||
};
|
||||
|
||||
let data = "";
|
||||
const decoder = new TextDecoder();
|
||||
const generic = {
|
||||
isatty: false,
|
||||
write(buffer) {
|
||||
data = decoder.decode(buffer);
|
||||
readline.write(data);
|
||||
return buffer.length;
|
||||
},
|
||||
};
|
||||
interpreter.setStdout(generic);
|
||||
interpreter.setStderr(generic);
|
||||
interpreter.setStdin({
|
||||
isatty: false,
|
||||
stdin: () => readline.read(data),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -286,7 +286,7 @@ class Element(BaseElement):
|
||||
canvas._js.width = width
|
||||
canvas._js.height = height
|
||||
|
||||
elif isistance(to, Element):
|
||||
elif isinstance(to, Element):
|
||||
if to._js.tagName != "CANVAS":
|
||||
raise TypeError("Element to snap to must a canvas.")
|
||||
canvas = to
|
||||
|
||||
@@ -4,7 +4,21 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../dist/core.css">
|
||||
<script type="module" src="../../dist/core.js"></script>
|
||||
<script type="module">
|
||||
import '../../dist/core.js';
|
||||
|
||||
addEventListener('mpy-editor', async ({ target }) => {
|
||||
if (target.hasAttribute('setup')) {
|
||||
await target.process([
|
||||
'from pyscript import document',
|
||||
// adds class="a-1" to the <html> element
|
||||
'document.documentElement.classList.add(f"a-{a}")',
|
||||
'from js import console',
|
||||
'console.log("Hello JS")',
|
||||
].join('\n'));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- a setup node with a config for an env -->
|
||||
|
||||
17
pyscript.core/test/py-editor/issue-2056.html
Normal file
17
pyscript.core/test/py-editor/issue-2056.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="../../dist/core.css">
|
||||
<script type="module" src="../../dist/core.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="py-editor">
|
||||
print("Hello!")
|
||||
</script>
|
||||
<script type="mpy-editor">
|
||||
print("Hello!")
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,6 +10,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<script type="py" src="terminal.py" worker terminal></script>
|
||||
<script type="py" src="terminal.py" worker terminal></script>
|
||||
<script type="mpy" src="terminal.py" worker terminal></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
3
pyscript.core/types/core.d.ts
vendored
3
pyscript.core/types/core.d.ts
vendored
@@ -1,6 +1,7 @@
|
||||
export function offline_interpreter(config: any): string;
|
||||
import { stdlib } from "./stdlib.js";
|
||||
import { optional } from "./stdlib.js";
|
||||
import { inputFailure } from "./hooks.js";
|
||||
import TYPES from "./types.js";
|
||||
/**
|
||||
* A `Worker` facade able to bootstrap on the worker thread only a PyScript module.
|
||||
@@ -53,4 +54,4 @@ declare const exportedHooks: {
|
||||
};
|
||||
declare const exportedConfig: {};
|
||||
declare const exportedWhenDefined: (type: string) => Promise<any>;
|
||||
export { stdlib, optional, TYPES, exportedPyWorker as PyWorker, exportedMPWorker as MPWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };
|
||||
export { stdlib, optional, inputFailure, TYPES, exportedPyWorker as PyWorker, exportedMPWorker as MPWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };
|
||||
|
||||
1
pyscript.core/types/hooks.d.ts
vendored
1
pyscript.core/types/hooks.d.ts
vendored
@@ -2,6 +2,7 @@ export function main(name: any): any;
|
||||
export function worker(name: any): any;
|
||||
export function codeFor(branch: any, type: any): {};
|
||||
export function createFunction(self: any, name: any): any;
|
||||
export const inputFailure: "\n import builtins\n def input(prompt=\"\"):\n raise Exception(\"\\n \".join([\n \"input() doesn't work when PyScript runs in the main thread.\",\n \"Consider using the worker attribute: https://pyscript.github.io/docs/2023.11.2/user-guide/workers/\"\n ]))\n\n builtins.input = input\n del builtins\n del input\n";
|
||||
export namespace hooks {
|
||||
namespace main {
|
||||
let onWorker: Set<Function>;
|
||||
|
||||
2
pyscript.core/types/plugins/py-terminal/mpy.d.ts
vendored
Normal file
2
pyscript.core/types/plugins/py-terminal/mpy.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
declare function _default(element: any): Promise<void>;
|
||||
export default _default;
|
||||
2
pyscript.core/types/plugins/py-terminal/py.d.ts
vendored
Normal file
2
pyscript.core/types/plugins/py-terminal/py.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
declare function _default(element: any): Promise<void>;
|
||||
export default _default;
|
||||
@@ -1,13 +1,13 @@
|
||||
black==23.11.0
|
||||
isort==5.12.0
|
||||
pytest==7.1.2
|
||||
pre-commit==3.5.0
|
||||
playwright==1.33.0
|
||||
pytest-playwright==0.3.3
|
||||
pytest-xdist==3.3.0
|
||||
black==24.4.2
|
||||
isort==5.13.2
|
||||
pytest==8.2.1
|
||||
pre-commit==3.7.1
|
||||
playwright==1.44.0
|
||||
pytest-playwright==0.5.0
|
||||
pytest-xdist==3.6.1
|
||||
pexpect==4.9.0
|
||||
pyodide_py==0.24.1
|
||||
micropip==0.5.0
|
||||
toml==0.10.2
|
||||
numpy==1.26.2
|
||||
pillow==10.1.0
|
||||
numpy==1.26.4
|
||||
pillow==10.3.0
|
||||
|
||||
Reference in New Issue
Block a user