mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-20 02:37:41 -05:00
Compare commits
19 Commits
event-refi
...
2025.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67fa31e4ea | ||
|
|
4937a46731 | ||
|
|
b4e9a3093c | ||
|
|
a129be8136 | ||
|
|
eaa6711756 | ||
|
|
b528ba67a9 | ||
|
|
71ad1a40cb | ||
|
|
e433275938 | ||
|
|
87256a662b | ||
|
|
7336ae545e | ||
|
|
d68260c0c7 | ||
|
|
14cc05fb80 | ||
|
|
42c6cb775e | ||
|
|
b11fb2e893 | ||
|
|
3223a9c7e9 | ||
|
|
139ce9b5fb | ||
|
|
3b1af0688c | ||
|
|
7284f7f15f | ||
|
|
16ebc50481 |
@@ -40,7 +40,7 @@ repos:
|
||||
- tomli
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.9.6
|
||||
rev: v0.11.8
|
||||
hooks:
|
||||
- id: ruff
|
||||
exclude: core/tests
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
ISSUE_TEMPLATE
|
||||
*.min.*
|
||||
package-lock.json
|
||||
bridge/
|
||||
|
||||
@@ -13,15 +13,15 @@ Using PyScript is as simple as:
|
||||
<title>PyScript!</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://pyscript.net/snapshots/2024.9.2/core.css"
|
||||
href="https://pyscript.net/releases/2025.8.1/core.css"
|
||||
/>
|
||||
<script
|
||||
type="module"
|
||||
src="https://pyscript.net/snapshots/2024.9.2/core.js"
|
||||
src="https://pyscript.net/releases/2025.8.1/core.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Use MicroPython to evaluate some Python -->
|
||||
<!-- type mpy (MicroPython) or py (Pyodide) to run some Python -->
|
||||
<script type="mpy" terminal>
|
||||
print("Hello, world!")
|
||||
</script>
|
||||
|
||||
57
bridge/README.md
Normal file
57
bridge/README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# @pyscript/bridge
|
||||
|
||||
Import Python utilities directly in JS
|
||||
|
||||
```js
|
||||
// main thread
|
||||
const { ffi: { func_a, func_b } } = await import('./test.js');
|
||||
|
||||
// test.js
|
||||
import bridge from 'https://esm.run/@pyscript/bridge';
|
||||
export const ffi = bridge(import.meta.url, { type: 'mpy', worker: false });
|
||||
|
||||
// test.py
|
||||
def func_a(value):
|
||||
print(f"hello {value}")
|
||||
|
||||
def func_b():
|
||||
import sys
|
||||
return sys.version
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
* **type**: `py` by default to bootstrap *Pyodide*.
|
||||
* **worker**: `true` by default to bootstrap in a *Web Worker*.
|
||||
* **config**: either a *string* or a PyScript compatible config *JS literal* to make it possible to bootstrap files and whatnot. If specified, the `worker` becomes implicitly `true` to avoid multiple configs conflicting on the main thread.
|
||||
* **env**: to share the same environment across multiple modules loaded at different times.
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
Run `npx mini-coi .` within this folder to then reach out `http://localhost:8080/test/` that will show:
|
||||
|
||||
```
|
||||
PyScript Bridge
|
||||
------------------
|
||||
no config
|
||||
```
|
||||
|
||||
The [test.js](./test/test.js) files uses the following defaults:
|
||||
|
||||
* `type` as `"mpy"`
|
||||
* `worker` as `false`
|
||||
* `config` as `undefined`
|
||||
* `env` as `undefined`
|
||||
|
||||
To test any variant use query string parameters so that `?type=py` will use `py` instead, `worker` will use a worker and `config` will use a basic *config* that brings in another file from the same folder which exposes the version.
|
||||
|
||||
To recap: `http://localhost:8080/test/?type=py&worker&config` will show this instead:
|
||||
|
||||
```
|
||||
PyScript Bridge
|
||||
------------------
|
||||
3.12.7 (main, May 15 2025, 18:47:24) ...
|
||||
```
|
||||
|
||||
Please note when a *config* is used, the `worker` attribute is always `true`.
|
||||
150
bridge/index.js
Normal file
150
bridge/index.js
Normal file
@@ -0,0 +1,150 @@
|
||||
/*! (c) PyScript Development Team */
|
||||
|
||||
const { stringify } = JSON;
|
||||
const { create, entries } = Object;
|
||||
|
||||
/**
|
||||
* Transform a list of keys into a Python dictionary.
|
||||
* `['a', 'b']` => `{ "a": a, "b": b }`
|
||||
* @param {Iterable<string>} keys
|
||||
* @returns {string}
|
||||
*/
|
||||
const dictionary = keys => {
|
||||
const fields = [];
|
||||
for (const key of keys)
|
||||
fields.push(`${stringify(key)}: ${key}`);
|
||||
return `{ ${fields.join(',')} }`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve properly config files relative URLs.
|
||||
* @param {string|Object} config - The configuration to normalize.
|
||||
* @param {string} base - The base URL to resolve relative URLs against.
|
||||
* @returns {string} - The JSON serialized config.
|
||||
*/
|
||||
const normalize = async (config, base) => {
|
||||
if (typeof config === 'string') {
|
||||
base = config;
|
||||
config = await fetch(config).then(res => res.json());
|
||||
}
|
||||
if (typeof config.files === 'object') {
|
||||
const files = {};
|
||||
for (const [key, value] of entries(config.files)) {
|
||||
files[key.startsWith('{') ? key : new URL(key, base)] = value;
|
||||
}
|
||||
config.files = files;
|
||||
}
|
||||
return stringify(config);
|
||||
};
|
||||
|
||||
// this logic is based on a 3 levels cache ...
|
||||
const cache = new Map;
|
||||
|
||||
/**
|
||||
* Return a bridge to a Python module via a `.js` file that has a `.py` alter ego.
|
||||
* @param {string} url - The URL of the JS module that has a Python counterpart.
|
||||
* @param {Object} options - The options for the bridge.
|
||||
* @param {string} [options.type='py'] - The `py` or `mpy` interpreter type, `py` by default.
|
||||
* @param {boolean} [options.worker=true] - Whether to use a worker, `true` by default.
|
||||
* @param {string|Object} [options.config=null] - The configuration for the bridge, `null` by default.
|
||||
* @param {string} [options.env=null] - The optional shared environment to use.
|
||||
* @param {string} [options.serviceWorker=null] - The optional service worker to use as fallback.
|
||||
* @returns {Object} - The bridge to the Python module.
|
||||
*/
|
||||
export default (url, {
|
||||
type = 'py',
|
||||
worker = true,
|
||||
config = null,
|
||||
env = null,
|
||||
serviceWorker = null,
|
||||
} = {}) => {
|
||||
const { protocol, host, pathname } = new URL(url);
|
||||
const py = pathname.replace(/\.m?js(?:\/\+\w+)?$/, '.py');
|
||||
const file = `${protocol}//${host}${py}`;
|
||||
|
||||
// the first cache is about the desired file in the wild ...
|
||||
if (!cache.has(file)) {
|
||||
// the second cache is about all fields one needs to access out there
|
||||
const exports = new Map;
|
||||
let python;
|
||||
|
||||
cache.set(file, new Proxy(create(null), {
|
||||
get(_, field) {
|
||||
if (!exports.has(field)) {
|
||||
// create an async callback once and always return the same later on
|
||||
exports.set(field, async (...args) => {
|
||||
// the third cache is about reaching lazily the code only once
|
||||
// augmenting its content with exports once and drop it on done
|
||||
if (!python) {
|
||||
// do not await or multiple calls will fetch multiple times
|
||||
// just assign the fetch `Promise` once and return it
|
||||
python = fetch(file).then(async response => {
|
||||
const code = await response.text();
|
||||
// create a unique identifier for the Python context
|
||||
const identifier = pathname.replace(/[^a-zA-Z0-9_]/g, '');
|
||||
const name = `__pyscript_${identifier}${Date.now()}`;
|
||||
// create a Python dictionary with all accessed fields
|
||||
const detail = `{"detail":${dictionary(exports.keys())}}`;
|
||||
// create the arguments for the `dispatchEvent` call
|
||||
const eventArgs = `${stringify(name)},${name}to_ts(${detail})`;
|
||||
// bootstrap the script element type and its attributes
|
||||
const script = document.createElement('script');
|
||||
script.type = type;
|
||||
|
||||
// if config is provided it needs to be a worker to avoid
|
||||
// conflicting with main config on the main thread (just like always)
|
||||
script.toggleAttribute('worker', !!config || !!worker);
|
||||
if (config) {
|
||||
const attribute = await normalize(config, file);
|
||||
script.setAttribute('config', attribute);
|
||||
}
|
||||
|
||||
if (env) script.setAttribute('env', env);
|
||||
if (serviceWorker) script.setAttribute('service-worker', serviceWorker);
|
||||
|
||||
// augment the code with the previously accessed fields at the end
|
||||
script.textContent = [
|
||||
'\n', code, '\n',
|
||||
// this is to avoid local scope name clashing
|
||||
`from pyscript import window as ${name}`,
|
||||
`from pyscript.ffi import to_js as ${name}to_ts`,
|
||||
`${name}.dispatchEvent(${name}.CustomEvent.new(${eventArgs}))`,
|
||||
// remove these references even if non-clashing to keep
|
||||
// the local scope clean from undesired entries
|
||||
`del ${name}`,
|
||||
`del ${name}to_ts`,
|
||||
].join('\n');
|
||||
|
||||
// let PyScript resolve and execute this script
|
||||
document.body.appendChild(script);
|
||||
|
||||
// intercept once the unique event identifier with all exports
|
||||
globalThis.addEventListener(
|
||||
name,
|
||||
event => {
|
||||
resolve(event.detail);
|
||||
script.remove();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
|
||||
// return a promise that will resolve only once the event
|
||||
// has been emitted and the interpreter evaluated the code
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
// return the `Promise` that will after invoke the exported field
|
||||
return python.then(foreign => foreign[field](...args));
|
||||
});
|
||||
}
|
||||
|
||||
// return the lazily to be resolved once callback to invoke
|
||||
return exports.get(field);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return cache.get(file);
|
||||
};
|
||||
27
bridge/package.json
Normal file
27
bridge/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@pyscript/bridge",
|
||||
"version": "0.1.0",
|
||||
"description": "A JS based way to use PyScript modules",
|
||||
"type": "module",
|
||||
"module": "./index.js",
|
||||
"unpkg": "./index.js",
|
||||
"jsdelivr": "./jsdelivr.js",
|
||||
"browser": "./index.js",
|
||||
"main": "./index.js",
|
||||
"keywords": [
|
||||
"PyScript",
|
||||
"JS",
|
||||
"Python",
|
||||
"bridge"
|
||||
],
|
||||
"author": "Anaconda Inc.",
|
||||
"license": "APACHE-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pyscript/pyscript.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/pyscript/pyscript/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pyscript/pyscript#readme"
|
||||
}
|
||||
33
bridge/test/index.html
Normal file
33
bridge/test/index.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>PyScript Bridge</title>
|
||||
<style>body { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; }</style>
|
||||
<link rel="stylesheet" href="https://pyscript.net/releases/2025.5.1/core.css" />
|
||||
<script type="module" src="https://pyscript.net/releases/2025.5.1/core.js"></script>
|
||||
<!-- for local testing purpose only-->
|
||||
<script type="importmap">{"imports":{"https://esm.run/@pyscript/bridge":"../index.js"}}</script>
|
||||
<script type="module">
|
||||
const { ffi: { test_func, test_other, version } } = await import('./test.js');
|
||||
|
||||
console.time("⏱️ first invoke");
|
||||
const result = await test_func("PyScript Bridge");
|
||||
console.timeEnd("⏱️ first invoke");
|
||||
|
||||
document.body.append(
|
||||
Object.assign(
|
||||
document.createElement("h3"),
|
||||
{ textContent: result },
|
||||
),
|
||||
document.createElement("hr"),
|
||||
await version(),
|
||||
);
|
||||
|
||||
console.time("⏱️ other invokes");
|
||||
await test_other("🐍");
|
||||
console.timeEnd("⏱️ other invokes");
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
||||
40
bridge/test/remote/index.html
Normal file
40
bridge/test/remote/index.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>PyScript Bridge</title>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"https://esm.run/@pyscript/bridge": "https://esm.run/@pyscript/bridge@latest",
|
||||
"https://esm.run/@pyscript/bridge/test/test.js": "https://esm.run/@pyscript/bridge@latest/test/test.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>body { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; }</style>
|
||||
<link rel="stylesheet" href="https://pyscript.net/releases/2025.5.1/core.css" />
|
||||
<script type="module" src="https://pyscript.net/releases/2025.5.1/core.js"></script>
|
||||
<script type="module">
|
||||
const cdn_test = 'https://esm.run/@pyscript/bridge/test/test.js';
|
||||
const { ffi: { test_func, test_other, version } } = await import(cdn_test);
|
||||
|
||||
console.time("⏱️ first invoke");
|
||||
const result = await test_func("PyScript Bridge");
|
||||
console.timeEnd("⏱️ first invoke");
|
||||
|
||||
document.body.append(
|
||||
Object.assign(
|
||||
document.createElement("h3"),
|
||||
{ textContent: result },
|
||||
),
|
||||
document.createElement("hr"),
|
||||
await version(),
|
||||
);
|
||||
|
||||
console.time("⏱️ other invokes");
|
||||
await test_other("🐍");
|
||||
console.timeEnd("⏱️ other invokes");
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
||||
5
bridge/test/sys_version.py
Normal file
5
bridge/test/sys_version.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import sys
|
||||
|
||||
|
||||
def version():
|
||||
return sys.version
|
||||
17
bridge/test/test.js
Normal file
17
bridge/test/test.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import bridge from "https://esm.run/@pyscript/bridge";
|
||||
|
||||
// for local testing purpose only
|
||||
const { searchParams } = new URL(location.href);
|
||||
|
||||
// the named (or default) export for test.py
|
||||
export const ffi = bridge(import.meta.url, {
|
||||
env: searchParams.get("env"),
|
||||
type: searchParams.get("type") || "mpy",
|
||||
worker: searchParams.has("worker"),
|
||||
config: searchParams.has("config") ?
|
||||
({
|
||||
files: {
|
||||
"./sys_version.py": "./sys_version.py",
|
||||
},
|
||||
}) : undefined,
|
||||
});
|
||||
22
bridge/test/test.py
Normal file
22
bridge/test/test.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from pyscript import config, RUNNING_IN_WORKER
|
||||
|
||||
type = config["type"]
|
||||
print(f"{type}-script", RUNNING_IN_WORKER and "worker" or "main")
|
||||
|
||||
|
||||
def test_func(message):
|
||||
print("Python", message)
|
||||
return message
|
||||
|
||||
|
||||
def test_other(message):
|
||||
print("Python", message)
|
||||
return message
|
||||
|
||||
|
||||
def version():
|
||||
try:
|
||||
from sys_version import version
|
||||
except ImportError:
|
||||
version = lambda: "no config"
|
||||
return version()
|
||||
205
core/LICENSE
Normal file
205
core/LICENSE
Normal file
@@ -0,0 +1,205 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
|
||||
Copyright (c) 2022-present, PyScript Development Team
|
||||
|
||||
Originated at Anaconda, Inc. in 2022
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
735
core/package-lock.json
generated
735
core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.6.39",
|
||||
"version": "0.6.70",
|
||||
"type": "module",
|
||||
"description": "PyScript",
|
||||
"module": "./index.js",
|
||||
@@ -35,11 +35,14 @@
|
||||
"./storage": {
|
||||
"import": "./dist/storage.js"
|
||||
},
|
||||
"./service-worker": {
|
||||
"import": "./dist/service-worker.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"server": "echo \"➡️ TESTS @ $(tput bold)http://localhost:8080/tests/$(tput sgr0)\"; npx static-handler --coi .",
|
||||
"build": "export ESLINT_USE_FLAT_CONFIG=true;npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && npm run build:tests-index && if [ -z \"$NO_MIN\" ]; then eslint src/ && npm run ts && npm run test:integration; fi",
|
||||
"build": "export ESLINT_USE_FLAT_CONFIG=true;npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && npm run build:tests-index && if [ -z \"$NO_MIN\" ]; then eslint src/ && npm run test:integration; fi",
|
||||
"build:core": "rm -rf dist && rollup --config rollup/core.config.js && cp src/3rd-party/*.css dist/",
|
||||
"build:flatted": "node rollup/flatted.cjs",
|
||||
"build:plugins": "node rollup/plugins.cjs",
|
||||
@@ -64,40 +67,40 @@
|
||||
"dependencies": {
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"@webreflection/idb-map": "^0.3.2",
|
||||
"@webreflection/utils": "^0.1.1",
|
||||
"add-promise-listener": "^0.1.3",
|
||||
"basic-devtools": "^0.1.6",
|
||||
"polyscript": "^0.16.22",
|
||||
"sabayon": "^0.6.6",
|
||||
"polyscript": "^0.18.9",
|
||||
"sticky-module": "^0.1.1",
|
||||
"to-json-callback": "^0.1.1",
|
||||
"type-checked-collections": "^0.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/commands": "^6.8.0",
|
||||
"@codemirror/lang-python": "^6.1.7",
|
||||
"@codemirror/language": "^6.11.0",
|
||||
"@codemirror/commands": "^6.8.1",
|
||||
"@codemirror/lang-python": "^6.2.1",
|
||||
"@codemirror/language": "^6.11.2",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.36.4",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.3",
|
||||
"@codemirror/view": "^6.38.1",
|
||||
"@playwright/test": "^1.54.2",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@webreflection/toml-j0.4": "^1.1.3",
|
||||
"@webreflection/toml-j0.4": "^1.1.4",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"bun": "^1.2.5",
|
||||
"bun": "^1.2.19",
|
||||
"chokidar": "^4.0.3",
|
||||
"codedent": "^0.1.2",
|
||||
"codemirror": "^6.0.1",
|
||||
"eslint": "^9.22.0",
|
||||
"codemirror": "^6.0.2",
|
||||
"eslint": "^9.32.0",
|
||||
"flatted": "^3.3.3",
|
||||
"rollup": "^4.36.0",
|
||||
"rollup": "^4.46.2",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"static-handler": "^0.5.3",
|
||||
"string-width": "^7.2.0",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript": "^5.9.2",
|
||||
"xterm-readline": "^1.1.2"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -53,4 +53,15 @@ export default [
|
||||
sourcemap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "./src/service-worker.js",
|
||||
plugins: plugins.concat(
|
||||
process.env.NO_MIN
|
||||
? [nodeResolve(), commonjs()]
|
||||
: [nodeResolve(), commonjs(), terser()],
|
||||
),
|
||||
output: {
|
||||
file: "./dist/service-worker.js",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
21
core/src/3rd-party-licenses/codemirror.license.txt
Normal file
21
core/src/3rd-party-licenses/codemirror.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijnh@gmail.com> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
21
core/src/3rd-party-licenses/codemirror_commands.license.txt
Normal file
21
core/src/3rd-party-licenses/codemirror_commands.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
21
core/src/3rd-party-licenses/codemirror_language.license.txt
Normal file
21
core/src/3rd-party-licenses/codemirror_language.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
21
core/src/3rd-party-licenses/codemirror_state.license.txt
Normal file
21
core/src/3rd-party-licenses/codemirror_state.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
21
core/src/3rd-party-licenses/codemirror_view.license.txt
Normal file
21
core/src/3rd-party-licenses/codemirror_view.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
21
core/src/3rd-party-licenses/toml.license.txt
Normal file
21
core/src/3rd-party-licenses/toml.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Jak Wings
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
25
core/src/3rd-party-licenses/xterm-readline.license.txt
Normal file
25
core/src/3rd-party-licenses/xterm-readline.license.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright 2021 Erik Bremen
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
21
core/src/3rd-party-licenses/xterm.license.txt
Normal file
21
core/src/3rd-party-licenses/xterm.license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
|
||||
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
19
core/src/3rd-party-licenses/xterm_addon-fit.license.txt
Normal file
19
core/src/3rd-party-licenses/xterm_addon-fit.license.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
4
core/src/3rd-party/README.md
vendored
4
core/src/3rd-party/README.md
vendored
@@ -5,3 +5,7 @@ This folder contains artifacts created via [3rd-party.cjs](../../rollup/3rd-part
|
||||
As we would like to offer a way to run PyScript offline, and we already offer a `dist` folder with all the necessary scripts, we have created a foreign dependencies resolver that allow to lazy-load CDN dependencies out of the box.
|
||||
|
||||
Please **note** these dependencies are **not interpreters**, because interpreters have their own mechanism, folders structure, WASM files, and whatnot, to work locally, but at least XTerm or the TOML parser, among other lazy dependencies, should be available within the dist folder.
|
||||
|
||||
## Licenses
|
||||
|
||||
All licenses provided by 3rd-party authors can be found in [3rd-party-licenses](../3rd-party-licenses/) folder.
|
||||
|
||||
2
core/src/3rd-party/xterm-readline.js
vendored
2
core/src/3rd-party/xterm-readline.js
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
|
||||
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
|
||||
* Original file: /npm/xterm-readline@1.1.2/lib/readline.js
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
|
||||
2
core/src/3rd-party/xterm.css
vendored
2
core/src/3rd-party/xterm.css
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Minified by jsDelivr using clean-css v5.3.2.
|
||||
* Minified by jsDelivr using clean-css v5.3.3.
|
||||
* Original file: /npm/@xterm/xterm@5.5.0/css/xterm.css
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
|
||||
4
core/src/3rd-party/xterm.js
vendored
4
core/src/3rd-party/xterm.js
vendored
File diff suppressed because one or more lines are too long
2
core/src/3rd-party/xterm_addon-fit.js
vendored
2
core/src/3rd-party/xterm_addon-fit.js
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.37.0.
|
||||
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
|
||||
* Original file: /npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
|
||||
2
core/src/3rd-party/xterm_addon-web-links.js
vendored
2
core/src/3rd-party/xterm_addon-web-links.js
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.37.0.
|
||||
* Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.
|
||||
* Original file: /npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.js
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import withResolvers from "@webreflection/utils/with-resolvers";
|
||||
import TYPES from "./types.js";
|
||||
|
||||
const waitForIt = [];
|
||||
@@ -5,7 +6,7 @@ const waitForIt = [];
|
||||
for (const [TYPE] of TYPES) {
|
||||
const selectors = [`script[type="${TYPE}"]`, `${TYPE}-script`];
|
||||
for (const element of document.querySelectorAll(selectors.join(","))) {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
const { promise, resolve } = withResolvers();
|
||||
waitForIt.push(promise);
|
||||
element.addEventListener(`${TYPE}:done`, resolve, { once: true });
|
||||
}
|
||||
|
||||
@@ -154,6 +154,9 @@ for (const [TYPE] of TYPES) {
|
||||
return await Promise.all(toBeAwaited);
|
||||
};
|
||||
|
||||
if (Number.isSafeInteger(parsed?.experimental_ffi_timeout))
|
||||
globalThis.reflected_ffi_timeout = parsed?.experimental_ffi_timeout;
|
||||
|
||||
configs.set(TYPE, { config: parsed, configURL, plugins, error });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/*! (c) PyScript Development Team */
|
||||
|
||||
import "./zero-redirect.js";
|
||||
import stickyModule from "sticky-module";
|
||||
import "@ungap/with-resolvers";
|
||||
import withResolvers from "@webreflection/utils/with-resolvers";
|
||||
|
||||
import {
|
||||
INVALID_CONTENT,
|
||||
@@ -327,7 +328,7 @@ for (const [TYPE, interpreter] of TYPES) {
|
||||
class extends HTMLElement {
|
||||
constructor() {
|
||||
assign(super(), {
|
||||
_wrap: Promise.withResolvers(),
|
||||
_wrap: withResolvers(),
|
||||
srcCode: "",
|
||||
executed: false,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import IDBMap from "@webreflection/idb-map";
|
||||
import withResolvers from "@webreflection/utils/with-resolvers";
|
||||
import { assign } from "polyscript/exports";
|
||||
import { $$ } from "basic-devtools";
|
||||
|
||||
@@ -26,7 +27,7 @@ export const getFileSystemDirectoryHandle = async (options) => {
|
||||
);
|
||||
}
|
||||
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const { promise, resolve, reject } = withResolvers();
|
||||
|
||||
const how = { id: "pyscript", mode: "readwrite", ...options };
|
||||
if (options.hint) how.startIn = options.hint;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// PyScript py-editor plugin
|
||||
import withResolvers from "@webreflection/utils/with-resolvers";
|
||||
import { Hook, XWorker, dedent, defineProperties } from "polyscript/exports";
|
||||
import { TYPES, offline_interpreter, relative_url, stdlib } from "../core.js";
|
||||
import { notify } from "./error.js";
|
||||
@@ -37,7 +38,7 @@ const getRelatedScript = (target, type) => {
|
||||
return editor?.parentNode?.previousElementSibling;
|
||||
};
|
||||
|
||||
async function execute({ currentTarget }) {
|
||||
async function execute({ currentTarget, script }) {
|
||||
const { env, pySrc, outDiv } = this;
|
||||
const hasRunButton = !!currentTarget;
|
||||
|
||||
@@ -90,16 +91,15 @@ async function execute({ currentTarget }) {
|
||||
// creation and destruction of editors on the fly
|
||||
if (hasRunButton) {
|
||||
for (const type of TYPES.keys()) {
|
||||
const script = getRelatedScript(currentTarget, type);
|
||||
if (script) {
|
||||
defineProperties(script, { xworker: { value: xworker } });
|
||||
break;
|
||||
}
|
||||
script = getRelatedScript(currentTarget, type);
|
||||
if (script) break;
|
||||
}
|
||||
}
|
||||
|
||||
defineProperties(script, { xworker: { value: xworker } });
|
||||
|
||||
const { sync } = xworker;
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
const { promise, resolve } = withResolvers();
|
||||
envs.set(env, promise);
|
||||
sync.revoke = () => {
|
||||
URL.revokeObjectURL(srcLink);
|
||||
@@ -126,6 +126,14 @@ async function execute({ currentTarget }) {
|
||||
if (hasRunButton) {
|
||||
currentTarget.classList.remove("running");
|
||||
currentTarget.innerHTML = RUN_BUTTON;
|
||||
const { previousElementSibling } =
|
||||
currentTarget.closest("[data-env]").parentElement;
|
||||
previousElementSibling?.dispatchEvent(
|
||||
new Event("py-editor:done", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
const { sync } = xworker;
|
||||
@@ -148,6 +156,20 @@ async function execute({ currentTarget }) {
|
||||
});
|
||||
}
|
||||
|
||||
const replaceScript = (script, type) => {
|
||||
script.xworker?.terminate();
|
||||
const clone = script.cloneNode(true);
|
||||
clone.type = `${type}-editor`;
|
||||
const editor = editors.get(script);
|
||||
if (editor) {
|
||||
const content = editor.state.doc.toString();
|
||||
clone.textContent = content;
|
||||
editors.delete(script);
|
||||
script.nextElementSibling.remove();
|
||||
}
|
||||
script.replaceWith(clone);
|
||||
};
|
||||
|
||||
const makeRunButton = (handler, type) => {
|
||||
const runButton = document.createElement("button");
|
||||
runButton.className = `absolute ${type}-editor-run-button`;
|
||||
@@ -160,15 +182,25 @@ const makeRunButton = (handler, type) => {
|
||||
) {
|
||||
const script = getRelatedScript(runButton, type);
|
||||
if (script) {
|
||||
const editor = editors.get(script);
|
||||
const content = editor.state.doc.toString();
|
||||
const clone = script.cloneNode(true);
|
||||
clone.type = `${type}-editor`;
|
||||
clone.textContent = content;
|
||||
script.xworker.terminate();
|
||||
script.nextElementSibling.remove();
|
||||
script.replaceWith(clone);
|
||||
editors.delete(script);
|
||||
const env = script.getAttribute("env");
|
||||
// remove the bootstrapped env which could be one or shared
|
||||
if (env) {
|
||||
for (const [key, value] of TYPES) {
|
||||
if (key === type) {
|
||||
configs.delete(`${value}-${env}`);
|
||||
envs.delete(`${value}-${env}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// lonley script without setup node should be replaced
|
||||
if (script.xworker) replaceScript(script, type);
|
||||
// all scripts sharing the same env should be replaced
|
||||
else {
|
||||
const sel = `script[type^="${type}-editor"][env="${env}"]`;
|
||||
for (const script of document.querySelectorAll(sel))
|
||||
replaceScript(script, type);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -356,7 +388,7 @@ const init = async (script, type, interpreter) => {
|
||||
};
|
||||
|
||||
if (isSetup) {
|
||||
await context.handleEvent({ currentTarget: null });
|
||||
await context.handleEvent({ currentTarget: null, script });
|
||||
notifyEditor();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// PyScript pyodide terminal plugin
|
||||
import withResolvers from "@webreflection/utils/with-resolvers";
|
||||
import { defineProperties } from "polyscript/exports";
|
||||
import { hooks, inputFailure } from "../../core.js";
|
||||
|
||||
@@ -146,7 +147,7 @@ export default async (element) => {
|
||||
// frees the worker on \r
|
||||
sync.pyterminal_read = (buffer) => {
|
||||
terminal.write(buffer);
|
||||
promisedChunks = Promise.withResolvers();
|
||||
promisedChunks = withResolvers();
|
||||
return promisedChunks.promise;
|
||||
};
|
||||
|
||||
|
||||
1
core/src/service-worker.js
Normal file
1
core/src/service-worker.js
Normal file
@@ -0,0 +1 @@
|
||||
import "polyscript/service-worker";
|
||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@ import io
|
||||
import re
|
||||
|
||||
from pyscript.magic_js import current_target, document, window
|
||||
from pyscript.ffi import is_none
|
||||
|
||||
_MIME_METHODS = {
|
||||
"savefig": "image/png",
|
||||
@@ -105,13 +106,13 @@ def _format_mime(obj):
|
||||
else:
|
||||
output = _eval_formatter(obj, method)
|
||||
|
||||
if output is None:
|
||||
if is_none(output):
|
||||
continue
|
||||
if mime_type not in _MIME_RENDERERS:
|
||||
not_available.append(mime_type)
|
||||
continue
|
||||
break
|
||||
if output is None:
|
||||
if is_none(output):
|
||||
if not_available:
|
||||
window.console.warn(
|
||||
f"Rendered object requested unavailable MIME renderers: {not_available}"
|
||||
@@ -135,7 +136,7 @@ def _write(element, value, append=False):
|
||||
element.append(out_element)
|
||||
else:
|
||||
out_element = element.lastElementChild
|
||||
if out_element is None:
|
||||
if is_none(out_element):
|
||||
out_element = element
|
||||
|
||||
if mime_type in ("application/javascript", "text/html"):
|
||||
@@ -146,7 +147,7 @@ def _write(element, value, append=False):
|
||||
|
||||
|
||||
def display(*values, target=None, append=True):
|
||||
if target is None:
|
||||
if is_none(target):
|
||||
target = current_target()
|
||||
elif not isinstance(target, str):
|
||||
msg = f"target must be str or None, not {target.__class__.__name__}"
|
||||
@@ -162,7 +163,7 @@ def display(*values, target=None, append=True):
|
||||
element = document.getElementById(target)
|
||||
|
||||
# If target cannot be found on the page, a ValueError is raised
|
||||
if element is None:
|
||||
if is_none(element):
|
||||
msg = f"Invalid selector with id={target}. Cannot be found in the page."
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ try:
|
||||
import js
|
||||
from pyodide.ffi import create_proxy as _cp
|
||||
from pyodide.ffi import to_js as _py_tjs
|
||||
from pyodide.ffi import jsnull
|
||||
|
||||
from_entries = js.Object.fromEntries
|
||||
is_none = lambda value: value is None or value is jsnull
|
||||
|
||||
def _tjs(value, **kw):
|
||||
if not hasattr(kw, "dict_converter"):
|
||||
@@ -13,6 +15,34 @@ try:
|
||||
except:
|
||||
from jsffi import create_proxy as _cp
|
||||
from jsffi import to_js as _tjs
|
||||
import js
|
||||
|
||||
jsnull = js.Object.getPrototypeOf(js.Object.prototype)
|
||||
is_none = lambda value: value is None or value is jsnull
|
||||
|
||||
create_proxy = _cp
|
||||
to_js = _tjs
|
||||
|
||||
try:
|
||||
from polyscript import ffi as _ffi
|
||||
|
||||
direct = _ffi.direct
|
||||
gather = _ffi.gather
|
||||
query = _ffi.query
|
||||
|
||||
def assign(source, *args):
|
||||
for arg in args:
|
||||
_ffi.assign(source, to_js(arg))
|
||||
return source
|
||||
|
||||
except:
|
||||
import js
|
||||
|
||||
_assign = js.Object.assign
|
||||
|
||||
direct = lambda source: source
|
||||
|
||||
def assign(source, *args):
|
||||
for arg in args:
|
||||
_assign(source, to_js(arg))
|
||||
return source
|
||||
|
||||
@@ -49,6 +49,28 @@ async def mount(path, mode="readwrite", root="", id="pyscript"):
|
||||
mounted[path] = await interpreter.mountNativeFS(path, handler)
|
||||
|
||||
|
||||
async def revoke(path, id="pyscript"):
|
||||
from _pyscript import fs, interpreter
|
||||
from pyscript.magic_js import (
|
||||
RUNNING_IN_WORKER,
|
||||
sync,
|
||||
)
|
||||
|
||||
uid = f"{path}@{id}"
|
||||
|
||||
if RUNNING_IN_WORKER:
|
||||
had = sync.deleteFSHandler(uid)
|
||||
else:
|
||||
had = await fs.idb.has(uid)
|
||||
if had:
|
||||
had = await fs.idb.delete(uid)
|
||||
|
||||
if had:
|
||||
interpreter._module.FS.unmount(path)
|
||||
|
||||
return had
|
||||
|
||||
|
||||
async def sync(path):
|
||||
await mounted[path].syncfs()
|
||||
|
||||
|
||||
@@ -67,7 +67,11 @@ if RUNNING_IN_WORKER:
|
||||
|
||||
else:
|
||||
import _pyscript
|
||||
from _pyscript import PyWorker, js_import
|
||||
from _pyscript import PyWorker as _PyWorker, js_import
|
||||
from pyscript.ffi import to_js
|
||||
|
||||
def PyWorker(url, **kw):
|
||||
return _PyWorker(url, to_js(kw))
|
||||
|
||||
window = globalThis
|
||||
document = globalThis.document
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from polyscript import storage as _storage
|
||||
from pyscript.flatted import parse as _parse
|
||||
from pyscript.flatted import stringify as _stringify
|
||||
from pyscript.ffi import is_none
|
||||
|
||||
|
||||
# convert a Python value into an IndexedDB compatible entry
|
||||
def _to_idb(value):
|
||||
if value is None:
|
||||
if is_none(value):
|
||||
return _stringify(["null", 0])
|
||||
if isinstance(value, (bool, float, int, str, list, dict, tuple)):
|
||||
return _stringify(["generic", value])
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# from __future__ import annotations # CAUTION: This is not supported in MicroPython.
|
||||
|
||||
from pyscript import document, when, Event # noqa: F401
|
||||
from pyscript.ffi import create_proxy
|
||||
from pyscript.ffi import create_proxy, is_none
|
||||
|
||||
|
||||
def wrap_dom_element(dom_element):
|
||||
@@ -68,8 +68,10 @@ class Element:
|
||||
If `dom_element` is None we are being called to *create* a new element.
|
||||
Otherwise, we are being called to *wrap* an existing DOM element.
|
||||
"""
|
||||
self._dom_element = dom_element or document.createElement(
|
||||
type(self).get_tag_name()
|
||||
self._dom_element = (
|
||||
document.createElement(type(self).get_tag_name())
|
||||
if is_none(dom_element)
|
||||
else dom_element
|
||||
)
|
||||
|
||||
# HTML on_events attached to the element become pyscript.Event instances.
|
||||
@@ -124,6 +126,11 @@ class Element:
|
||||
# Element instance via `for_`).
|
||||
if name.endswith("_"):
|
||||
name = name[:-1] # noqa: FURB188 No str.removesuffix() in MicroPython.
|
||||
if name == "for":
|
||||
# The `for` attribute is a special case as it is a keyword in both
|
||||
# Python and JavaScript.
|
||||
# We need to get it from the underlying DOM element as `htmlFor`.
|
||||
name = "htmlFor"
|
||||
return getattr(self._dom_element, name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
@@ -142,6 +149,11 @@ class Element:
|
||||
# Element instance via `for_`).
|
||||
if name.endswith("_"):
|
||||
name = name[:-1] # noqa: FURB188 No str.removesuffix() in MicroPython.
|
||||
if name == "for":
|
||||
# The `for` attribute is a special case as it is a keyword in both
|
||||
# Python and JavaScript.
|
||||
# We need to set it on the underlying DOM element as `htmlFor`.
|
||||
name = "htmlFor"
|
||||
|
||||
if name.startswith("on_"):
|
||||
# Ensure on-events are cached in the _on_events dict if the
|
||||
@@ -185,7 +197,7 @@ class Element:
|
||||
@property
|
||||
def parent(self):
|
||||
"""Return the element's `parent `Element`."""
|
||||
if self._dom_element.parentElement is None:
|
||||
if is_none(self._dom_element.parentElement):
|
||||
return None
|
||||
|
||||
return Element.wrap_dom_element(self._dom_element.parentElement)
|
||||
@@ -1124,7 +1136,7 @@ class video(ContainerElement):
|
||||
width = width if width is not None else self.videoWidth
|
||||
height = height if height is not None else self.videoHeight
|
||||
|
||||
if to is None:
|
||||
if is_none(to):
|
||||
to = canvas(width=width, height=height)
|
||||
|
||||
elif isinstance(to, Element):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import js
|
||||
from pyscript.ffi import create_proxy
|
||||
from pyscript.util import as_bytearray
|
||||
from pyscript.util import as_bytearray, is_awaitable
|
||||
|
||||
code = "code"
|
||||
protocols = "protocols"
|
||||
@@ -8,6 +8,23 @@ reason = "reason"
|
||||
methods = ["onclose", "onerror", "onmessage", "onopen"]
|
||||
|
||||
|
||||
def add_listener(socket, onevent, listener):
|
||||
p = create_proxy(listener)
|
||||
|
||||
if is_awaitable(listener):
|
||||
|
||||
async def wrapper(e):
|
||||
await p(EventMessage(e))
|
||||
|
||||
m = wrapper
|
||||
|
||||
else:
|
||||
m = lambda e: p(EventMessage(e))
|
||||
|
||||
# Pyodide fails at setting socket[onevent] directly
|
||||
setattr(socket, onevent, m)
|
||||
|
||||
|
||||
class EventMessage:
|
||||
def __init__(self, event):
|
||||
self._event = event
|
||||
@@ -36,20 +53,20 @@ class WebSocket:
|
||||
socket = js.WebSocket.new(url, kw[protocols])
|
||||
else:
|
||||
socket = js.WebSocket.new(url)
|
||||
|
||||
socket.binaryType = "arraybuffer"
|
||||
object.__setattr__(self, "_ws", socket)
|
||||
|
||||
for t in methods:
|
||||
if t in kw:
|
||||
# Pyodide fails at setting socket[t] directly
|
||||
setattr(socket, t, create_proxy(kw[t]))
|
||||
add_listener(socket, t, kw[t])
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self._ws, attr)
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr in methods:
|
||||
m = lambda e: value(EventMessage(e))
|
||||
setattr(self._ws, attr, create_proxy(m))
|
||||
add_listener(self._ws, attr, value)
|
||||
else:
|
||||
setattr(self._ws, attr, value)
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ArrayBuffer, TypedArray } from "sabayon/shared";
|
||||
import IDBMapSync from "@webreflection/idb-map/sync";
|
||||
import { parse, stringify } from "flatted";
|
||||
|
||||
const { isView } = ArrayBuffer;
|
||||
|
||||
const to_idb = (value) => {
|
||||
if (value == null) return stringify(["null", 0]);
|
||||
/* eslint-disable no-fallthrough */
|
||||
switch (typeof value) {
|
||||
case "object": {
|
||||
if (value instanceof TypedArray)
|
||||
return stringify(["memoryview", [...value]]);
|
||||
if (isView(value)) return stringify(["memoryview", [...value]]);
|
||||
if (value instanceof ArrayBuffer)
|
||||
return stringify(["bytearray", [...new Uint8Array(value)]]);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
* Ask a user action via dialog and returns the directory handler once granted.
|
||||
* @param {string} uid
|
||||
* @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options
|
||||
* @returns {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async storeFSHandler(uid, options = {}) {
|
||||
if (await idb.has(uid)) return true;
|
||||
@@ -28,4 +28,15 @@ export default {
|
||||
() => false,
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Explicitly remove the unique identifier for the FS handler.
|
||||
* @param {string} uid
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async deleteFSHandler(uid) {
|
||||
const had = await idb.has(uid);
|
||||
if (had) await idb.delete(uid);
|
||||
return had;
|
||||
},
|
||||
};
|
||||
|
||||
7
core/src/zero-redirect.js
Normal file
7
core/src/zero-redirect.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
try {
|
||||
crypto.randomUUID();
|
||||
} catch (_) {
|
||||
if (location.href.startsWith("http://0.0.0.0"))
|
||||
location.href = location.href.replace("0.0.0.0", "localhost");
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
36
core/tests/javascript/worker-symbols.html
Normal file
36
core/tests/javascript/worker-symbols.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PyScript VS Symbols</title>
|
||||
<script>
|
||||
globalThis.hasSymbol = (symbol, ref) => symbol in ref;
|
||||
globalThis.getSymbol = (symbol, ref) => ref[symbol];
|
||||
|
||||
// some 3rd party JS library might use symbols to brand-check
|
||||
// so it's not about symbols traveling from MicroPython
|
||||
// it's about MicroPython proxies traps not understanding symbols
|
||||
globalThis.hasIterator = ref => Symbol.iterator in ref;
|
||||
</script>
|
||||
<link rel="stylesheet" href="../../dist/core.css">
|
||||
<script type="module" src="../../dist/core.js"></script>
|
||||
<script type="mpy">
|
||||
import js
|
||||
|
||||
symbol = js.Symbol.iterator
|
||||
|
||||
if js.getSymbol(symbol, []) and js.hasSymbol(symbol, []) and js.hasIterator([]):
|
||||
js.document.documentElement.classList.add("main")
|
||||
</script>
|
||||
<script type="mpy" worker>
|
||||
from pyscript import window
|
||||
import js
|
||||
|
||||
symbol = js.Symbol.iterator
|
||||
|
||||
if window.getSymbol(symbol, []) and window.hasSymbol(symbol, []) and window.hasIterator([]):
|
||||
window.document.documentElement.classList.add("worker")
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
||||
@@ -14,7 +14,7 @@
|
||||
print(event.type)
|
||||
ws.send("hello")
|
||||
|
||||
def onmessage(event):
|
||||
async def onmessage(event):
|
||||
print(event.type, event.data)
|
||||
ws.close()
|
||||
|
||||
|
||||
@@ -59,6 +59,11 @@ test('MicroPython + configURL', async ({ page }) => {
|
||||
await page.waitForSelector('html.main.worker');
|
||||
});
|
||||
|
||||
test('MicroPython + Symbols', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080/tests/javascript/worker-symbols.html');
|
||||
await page.waitForSelector('html.main.worker');
|
||||
});
|
||||
|
||||
test('Pyodide + terminal on Main', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080/tests/javascript/py-terminal-main.html');
|
||||
await page.waitForSelector('html.ok');
|
||||
|
||||
21
core/tests/manual/ffi_timeout/index.html
Normal file
21
core/tests/manual/ffi_timeout/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="../../../dist/core.css">
|
||||
<script>
|
||||
window.Worker = class extends Worker {
|
||||
constructor(url, ...rest) {
|
||||
console.log(rest[0]);
|
||||
return super(url, ...rest);
|
||||
}
|
||||
};
|
||||
window.start = Date.now();
|
||||
</script>
|
||||
<script type="module" src="../../../dist/core.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="py" config="./index.toml" src="index.py" worker></script>
|
||||
</body>
|
||||
</html>
|
||||
3
core/tests/manual/ffi_timeout/index.py
Normal file
3
core/tests/manual/ffi_timeout/index.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from pyscript import document, window
|
||||
|
||||
document.body.append(window.Date.now() - window.start)
|
||||
2
core/tests/manual/ffi_timeout/index.toml
Normal file
2
core/tests/manual/ffi_timeout/index.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
experimental_ffi_timeout = 0
|
||||
package_cache = "passthrough"
|
||||
@@ -19,6 +19,8 @@ if TEST == "implicit":
|
||||
|
||||
await fs.sync("/persistent")
|
||||
|
||||
# await fs.revoke("/persistent")
|
||||
|
||||
elif not RUNNING_IN_WORKER:
|
||||
from pyscript import document
|
||||
|
||||
@@ -39,7 +41,7 @@ elif not RUNNING_IN_WORKER:
|
||||
js.alert("unable to grant access")
|
||||
|
||||
async def unmount(event):
|
||||
await fs.unmount("/persistent")
|
||||
await fs.revoke("/persistent")
|
||||
button.textContent = "mount"
|
||||
button.onclick = mount
|
||||
|
||||
|
||||
@@ -24,5 +24,6 @@
|
||||
"./example_js_worker_module.js": "greeting_worker"
|
||||
}
|
||||
},
|
||||
"packages": ["Pillow" ]
|
||||
"packages": ["Pillow" ],
|
||||
"experimental_ffi_timeout": 0
|
||||
}
|
||||
|
||||
@@ -871,7 +871,17 @@ class TestElements:
|
||||
self._create_el_and_basic_asserts("kbd", "some text")
|
||||
|
||||
def test_label(self):
|
||||
self._create_el_and_basic_asserts("label", "some text")
|
||||
label_text = "Luke, I am your father"
|
||||
label_for = "some-id"
|
||||
# Let's create the element
|
||||
el = web.label(label_text, for_=label_for)
|
||||
# Let's check the element was configured correctly.
|
||||
assert isinstance(el, web.label), "The new element should be a label."
|
||||
assert el.textContent == label_text, "The label text should match."
|
||||
assert el._dom_element.tagName == "LABEL"
|
||||
assert el.for_ == label_for, "The label should have the correct for attribute."
|
||||
# Ensure the label element is rendered with the correct "for" attribute
|
||||
assert f'for="{label_for}"' in el.outerHTML, "The label should have the correct 'for' attribute in its HTML."
|
||||
|
||||
def test_legend(self):
|
||||
self._create_el_and_basic_asserts("legend", "some text")
|
||||
|
||||
2
core/types/core.d.ts
vendored
2
core/types/core.d.ts
vendored
@@ -63,5 +63,5 @@ declare const exportedHooks: {
|
||||
};
|
||||
};
|
||||
declare const exportedConfig: {};
|
||||
declare const exportedWhenDefined: any;
|
||||
declare const exportedWhenDefined: (type: string) => Promise<object>;
|
||||
export { codemirror, stdlib, optional, inputFailure, TYPES, relative_url, exportedPyWorker as PyWorker, exportedMPWorker as MPWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };
|
||||
|
||||
10
core/types/sync.d.ts
vendored
10
core/types/sync.d.ts
vendored
@@ -9,12 +9,18 @@ declare namespace _default {
|
||||
* Ask a user action via dialog and returns the directory handler once granted.
|
||||
* @param {string} uid
|
||||
* @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options
|
||||
* @returns {boolean}
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function storeFSHandler(uid: string, options?: {
|
||||
id?: string;
|
||||
mode?: "read" | "readwrite";
|
||||
hint?: "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos";
|
||||
}): boolean;
|
||||
}): Promise<boolean>;
|
||||
/**
|
||||
* Explicitly remove the unique identifier for the FS handler.
|
||||
* @param {string} uid
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
function deleteFSHandler(uid: string): Promise<boolean>;
|
||||
}
|
||||
export default _default;
|
||||
|
||||
1
core/types/zero-redirect.d.ts
vendored
Normal file
1
core/types/zero-redirect.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -137,12 +137,14 @@
|
||||
<body>
|
||||
<h1>Hello world!</h1>
|
||||
<p>These are the Python interpreters in PyScript _VERSION_:</p>
|
||||
<script type="py"> <!-- Pyodide -->
|
||||
<script type="py">
|
||||
# Pyodide
|
||||
from pyscript import display
|
||||
import sys
|
||||
display(sys.version)
|
||||
</script>
|
||||
<script type="mpy"> <!-- MicroPython -->
|
||||
<script type="mpy">
|
||||
# MicroPython
|
||||
from pyscript import display
|
||||
import sys
|
||||
display(sys.version)
|
||||
|
||||
Reference in New Issue
Block a user