Compare commits

...

13 Commits

Author SHA1 Message Date
Nicholas H.Tollervey
58c91b941b Docstrings 2024-10-24 14:54:13 +01:00
pre-commit-ci[bot]
b33661ff8e [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2024-10-24 13:22:29 +00:00
Nicholas H.Tollervey
9db8b13d9c Revert lock change. 2024-10-24 14:20:14 +01:00
Nicholas H.Tollervey
3003a9671d First draft of a working when function/decorator. 2024-10-23 17:58:16 +01:00
Nicholas H.Tollervey
b87c86f266 Add two unit tests for illustrative purposes. 2024-10-23 11:11:49 +01:00
Andrea Giammarchi
6b1330d28a Fix #2220 - Avoid DOM notifications on errors (#2226)
* Fix #2220 - Avoid DOM notifications on errors
2024-10-17 16:18:05 +02:00
Carlovo
5d751493f6 Fix typo in README.md (#2219)
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2024-10-15 12:57:28 +02:00
Andrea Giammarchi
c3517f7973 Donkey clear and reset now terminate when busy (#2225)
* Donkey clear and reset now terminate when busy
2024-10-15 12:56:11 +02:00
Andrea Giammarchi
b1c33b7f79 Kill previous worker if another eval/execute is asked to the donkey (#2218)
Kill previous worker if another eval/execute is asked to the donkey
2024-10-11 16:12:56 +02:00
Andrea Giammarchi
722abda895 Provide an xworker to PyEditors (#2216) 2024-10-09 12:42:20 +02:00
Andrea Giammarchi
8061bc0143 Fixed typo on donkey code (#2213) 2024-10-08 17:17:51 +02:00
pre-commit-ci[bot]
993e812e7b [pre-commit.ci] pre-commit autoupdate (#2211)
* [pre-commit.ci] pre-commit autoupdate
* Fix broken pre-commit-config setting and bump version of black.

updates:
- [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0)
- [github.com/psf/black: 24.8.0 → 24.10.0](https://github.com/psf/black/compare/24.8.0...24.10.0)



---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Nicholas H.Tollervey <ntoll@ntoll.org>
2024-10-08 14:16:31 +01:00
Andrea Giammarchi
a6b6dd8479 Refactor named workers test to avoid circular dependencies + free CI (#2212)
* Refactor named workers test to avoid circular dependencies + free CI
2024-10-08 14:58:22 +02:00
38 changed files with 604 additions and 286 deletions

View File

@@ -11,7 +11,7 @@ defaults:
jobs: jobs:
prepare-release: prepare-release:
runs-on: ubuntu-latest-8core runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -10,7 +10,7 @@ defaults:
jobs: jobs:
publish-release: publish-release:
runs-on: ubuntu-latest-8core runs-on: ubuntu-latest
permissions: permissions:
id-token: write id-token: write
contents: read contents: read

View File

@@ -14,7 +14,7 @@ defaults:
jobs: jobs:
publish-snapshot: publish-snapshot:
runs-on: ubuntu-latest-8core runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
id-token: write id-token: write

View File

@@ -12,7 +12,7 @@ on:
jobs: jobs:
publish-unstable: publish-unstable:
runs-on: ubuntu-latest-8core runs-on: ubuntu-latest
permissions: permissions:
id-token: write id-token: write
contents: read contents: read

View File

@@ -18,7 +18,7 @@ on:
jobs: jobs:
BuildAndTest: BuildAndTest:
runs-on: ubuntu-latest-8core runs-on: ubuntu-latest
env: env:
MINICONDA_PYTHON_VERSION: py38 MINICONDA_PYTHON_VERSION: py38
MINICONDA_VERSION: 4.11.0 MINICONDA_VERSION: 4.11.0

View File

@@ -4,10 +4,10 @@ ci:
#skip: [eslint] #skip: [eslint]
autoupdate_schedule: monthly autoupdate_schedule: monthly
default_stages: [commit] default_stages: [pre-commit]
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 rev: v5.0.0
hooks: hooks:
- id: check-builtin-literals - id: check-builtin-literals
- id: check-case-conflict - id: check-case-conflict
@@ -25,7 +25,7 @@ repos:
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 24.8.0 rev: 24.10.0
hooks: hooks:
- id: black - id: black
args: ["-l", "88", "--skip-string-normalization"] args: ["-l", "88", "--skip-string-normalization"]

View File

@@ -1,6 +1,6 @@
# PyScript # PyScript
## PyScrcipt is an open source platform for Python in the browser. ## PyScript is an open source platform for Python in the browser.
Using PyScript is as simple as: Using PyScript is as simple as:
@@ -76,7 +76,7 @@ Read the [contributing guide](https://docs.pyscript.net/latest/contributing/)
to learn about our development process, reporting bugs and improvements, to learn about our development process, reporting bugs and improvements,
creating issues and asking questions. creating issues and asking questions.
Check out the [developing process](https://docs.pyscript.net/latest/developers/) Check out the [development process](https://docs.pyscript.net/latest/developers/)
documentation for more information on how to setup your development environment. documentation for more information on how to setup your development environment.
## Governance ## Governance

212
core/package-lock.json generated
View File

@@ -1,18 +1,19 @@
{ {
"name": "@pyscript/core", "name": "@pyscript/core",
"version": "0.6.2", "version": "0.6.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@pyscript/core", "name": "@pyscript/core",
"version": "0.6.2", "version": "0.6.7",
"license": "APACHE-2.0", "license": "APACHE-2.0",
"dependencies": { "dependencies": {
"@ungap/with-resolvers": "^0.1.0", "@ungap/with-resolvers": "^0.1.0",
"@webreflection/idb-map": "^0.3.2", "@webreflection/idb-map": "^0.3.2",
"add-promise-listener": "^0.1.3",
"basic-devtools": "^0.1.6", "basic-devtools": "^0.1.6",
"polyscript": "^0.16.2", "polyscript": "^0.16.3",
"sabayon": "^0.5.2", "sabayon": "^0.5.2",
"sticky-module": "^0.1.1", "sticky-module": "^0.1.1",
"to-json-callback": "^0.1.1", "to-json-callback": "^0.1.1",
@@ -25,13 +26,13 @@
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.34.1", "@codemirror/view": "^6.34.1",
"@playwright/test": "1.45.3", "@playwright/test": "1.45.3",
"@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@webreflection/toml-j0.4": "^1.1.3", "@webreflection/toml-j0.4": "^1.1.3",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0", "@xterm/addon-web-links": "^0.11.0",
"bun": "^1.1.29", "bun": "^1.1.30",
"chokidar": "^4.0.1", "chokidar": "^4.0.1",
"codedent": "^0.1.2", "codedent": "^0.1.2",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
@@ -42,7 +43,7 @@
"rollup-plugin-string": "^3.0.0", "rollup-plugin-string": "^3.0.0",
"static-handler": "^0.5.3", "static-handler": "^0.5.3",
"string-width": "^7.2.0", "string-width": "^7.2.0",
"typescript": "^5.6.2", "typescript": "^5.6.3",
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-readline": "^1.1.1" "xterm-readline": "^1.1.1"
}, },
@@ -431,9 +432,9 @@
} }
}, },
"node_modules/@oven/bun-darwin-aarch64": { "node_modules/@oven/bun-darwin-aarch64": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.1.30.tgz",
"integrity": "sha512-Z8WnrXoAXg6ENU6z+Mil94oipMBMliJIj4XWWqjNC33/ZGPqMdPT5MbFQjbdHjxe5bRBW/aHaMU3G35fGx9Seg==", "integrity": "sha512-D07QioP+QXlouvIqQIS+7r2zq4lTNd6he79rhKsRQRZGFf9i3NPu87zspUpCaFEu//DZ35DYTt+5anQpAzpoxA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -445,9 +446,9 @@
] ]
}, },
"node_modules/@oven/bun-darwin-x64": { "node_modules/@oven/bun-darwin-x64": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.1.30.tgz",
"integrity": "sha512-tDHDR+OWLCKEP0xE8IGCWRxSsxO5tr3rvyrSzy6CXYkoWPz66kODbtbGfWl8HSwunrQxJKnE2L9LWX2Eiz3Cpw==", "integrity": "sha512-xZ4gTehS6QwN6bsJfDycCNneKoUMaFUQhQg24bJzXS4JPDxeKg1W7PS5AE+U9apz5Dx6//+D4RwVpAPG2LXt0w==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -459,9 +460,9 @@
] ]
}, },
"node_modules/@oven/bun-darwin-x64-baseline": { "node_modules/@oven/bun-darwin-x64-baseline": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.1.30.tgz",
"integrity": "sha512-B+zKlJR+3ANPDU+vNjvXaqgB63hYcCk16/k45hiInvIsDXfSdDn4+LMHwZTFdfUWTCGbNMFg9u4bZCtK0nhMDw==", "integrity": "sha512-1kFUCxHx7WuEbLDmqm0m2UKBd3S4Ln6qKQ4gxU4umMLFkmvDJn6PszDruFInxGKFLoTAmbXNYNVWkkG/ekt/Lg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -473,9 +474,9 @@
] ]
}, },
"node_modules/@oven/bun-linux-aarch64": { "node_modules/@oven/bun-linux-aarch64": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.1.30.tgz",
"integrity": "sha512-BuV2Gni+FaMo1r7+3vLjefQYtQcWE01zJJ4zCjyzikP1L4r6PUaLeJyKXbqF6sIR0cLjG6LWj66EGskakDyvWA==", "integrity": "sha512-SfHHLlph6fptDXyyChcUkeDbEZr2ww1p2BucV6OrvzwTOPi8pVmXA4360YT8ggR/3AHPp4GO36VaD+FU2Ocbxw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -487,9 +488,9 @@
] ]
}, },
"node_modules/@oven/bun-linux-x64": { "node_modules/@oven/bun-linux-x64": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.1.30.tgz",
"integrity": "sha512-JVLiTafybuOFIeC80mZEzHdkOCCzlOtOV5zCbvYAIb7D3SM64fNBwrR0dvDkJTQtsjbwt8YeVFN2YRSI/wlFkw==", "integrity": "sha512-1mC39jQSaECytEKAZdCZmv3ZreMsp7aoxnBwmJtVd2Z7urnw17PKi4dKkZd/R+AubsNYtXtW4jeM8SEa5sUJRw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -501,9 +502,9 @@
] ]
}, },
"node_modules/@oven/bun-linux-x64-baseline": { "node_modules/@oven/bun-linux-x64-baseline": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.1.30.tgz",
"integrity": "sha512-tQhm1SDyymBvc2L3zIN7FI+sI3g9gxavDYuvL/Lz17nEk1oqP5DFE2lF9QWJMDdQL3XQKH/drI27yNpfPHgAPw==", "integrity": "sha512-/b/VuNOaAYmsVk9MvfwKcCYARJPUg78hebxNyD5DSajAf3dqtUSnf7QYcq/3mxWH++N+gM7uRTrGksGS63+ZUw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -515,9 +516,9 @@
] ]
}, },
"node_modules/@oven/bun-windows-x64": { "node_modules/@oven/bun-windows-x64": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.1.30.tgz",
"integrity": "sha512-d930+pSkNVHPJJBryIh1gsAKk7lp1HzcktFe0bfxwmpHwjj4BiVfMjU1YaCnsdWKNs2Nv1Y1PLVDNyUCQVfo0Q==", "integrity": "sha512-mdRjNtD9NIA8CiH6N1zrIVE6oAtDko/c29H1s00UA+5O/WhXhg95G8IyInD8hN3vAEz8H2lGBgLG2EGfSFxnGg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -529,9 +530,9 @@
] ]
}, },
"node_modules/@oven/bun-windows-x64-baseline": { "node_modules/@oven/bun-windows-x64-baseline": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.1.29.tgz", "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.1.30.tgz",
"integrity": "sha512-+QWT8Kp+3Sl54QUa6uBsDzQlX11thMMVAw+yUwSRU4Y5FVSN8RPuxhN8ijJ1QGm1KOYA2sCI6V+3lyNMNmRvAA==", "integrity": "sha512-ERQ4/ogzbFvHjpyHcnruc8bnryvDvUoiWi6vczfQ4M/idJc+Kg5VSEJiF5k7946rIZGamG6QWgRxtpIglD4/Zw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -559,19 +560,19 @@
} }
}, },
"node_modules/@rollup/plugin-commonjs": { "node_modules/@rollup/plugin-commonjs": {
"version": "28.0.0", "version": "28.0.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
"integrity": "sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg==", "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@rollup/pluginutils": "^5.0.1", "@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1", "commondir": "^1.0.1",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"fdir": "^6.1.1", "fdir": "^6.2.0",
"is-reference": "1.2.1", "is-reference": "1.2.1",
"magic-string": "^0.30.3", "magic-string": "^0.30.3",
"picomatch": "^2.3.1" "picomatch": "^4.0.2"
}, },
"engines": { "engines": {
"node": ">=16.0.0 || 14 >= 14.17" "node": ">=16.0.0 || 14 >= 14.17"
@@ -585,19 +586,6 @@
} }
} }
}, },
"node_modules/@rollup/plugin-commonjs/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/@rollup/plugin-node-resolve": { "node_modules/@rollup/plugin-node-resolve": {
"version": "15.3.0", "version": "15.3.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
@@ -1018,6 +1006,12 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/add-promise-listener": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/add-promise-listener/-/add-promise-listener-0.1.3.tgz",
"integrity": "sha512-hQ6IgGJ7NvvlPYbwdekhdVwPb4QzEptNZ5v7B4XRKz7FukUPDuF/v+R5EFHArWmhmq4d+xv0G4/B5bu2GSiz9Q==",
"license": "MIT"
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1036,13 +1030,16 @@
} }
}, },
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
"version": "5.0.1", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
} }
}, },
"node_modules/ansi-styles": { "node_modules/ansi-styles": {
@@ -1140,9 +1137,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/bun": { "node_modules/bun": {
"version": "1.1.29", "version": "1.1.30",
"resolved": "https://registry.npmjs.org/bun/-/bun-1.1.29.tgz", "resolved": "https://registry.npmjs.org/bun/-/bun-1.1.30.tgz",
"integrity": "sha512-SKhpyKNZtgxrVel9ec9xon3LDv8mgpiuFhARgcJo1YIbggY2PBrKHRNiwQ6Qlb+x3ivmRurfuwWgwGexjpgBRg==", "integrity": "sha512-ysRL1pq10Xba0jqVLPrKU3YIv0ohfp3cTajCPtpjCyppbn3lfiAVNpGoHfyaxS17OlPmWmR67UZRPw/EueQuug==",
"cpu": [ "cpu": [
"arm64", "arm64",
"x64" "x64"
@@ -1160,14 +1157,14 @@
"bunx": "bin/bun.exe" "bunx": "bin/bun.exe"
}, },
"optionalDependencies": { "optionalDependencies": {
"@oven/bun-darwin-aarch64": "1.1.29", "@oven/bun-darwin-aarch64": "1.1.30",
"@oven/bun-darwin-x64": "1.1.29", "@oven/bun-darwin-x64": "1.1.30",
"@oven/bun-darwin-x64-baseline": "1.1.29", "@oven/bun-darwin-x64-baseline": "1.1.30",
"@oven/bun-linux-aarch64": "1.1.29", "@oven/bun-linux-aarch64": "1.1.30",
"@oven/bun-linux-x64": "1.1.29", "@oven/bun-linux-x64": "1.1.30",
"@oven/bun-linux-x64-baseline": "1.1.29", "@oven/bun-linux-x64-baseline": "1.1.30",
"@oven/bun-windows-x64": "1.1.29", "@oven/bun-windows-x64": "1.1.30",
"@oven/bun-windows-x64-baseline": "1.1.29" "@oven/bun-windows-x64-baseline": "1.1.30"
} }
}, },
"node_modules/callsites": { "node_modules/callsites": {
@@ -1194,9 +1191,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001666", "version": "1.0.30001668",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001666.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz",
"integrity": "sha512-gD14ICmoV5ZZM1OdzPWmpx+q4GyefaK06zi8hmfHV5xe4/2nOQX3+Dw5o+fSqOws2xVwL9j+anOPFwHzdEdV4g==", "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -1623,9 +1620,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.31", "version": "1.5.36",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.31.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.36.tgz",
"integrity": "sha512-QcDoBbQeYt0+3CWcK/rEbuHvwpbT/8SV9T3OSgs6cX1FlcUAkgrkqbg9zLnDrMM/rLamzQwal4LYFCiWk861Tg==", "integrity": "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -2339,9 +2336,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.11", "version": "0.30.12",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
"integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2578,8 +2575,6 @@
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true,
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -2639,9 +2634,9 @@
} }
}, },
"node_modules/polyscript": { "node_modules/polyscript": {
"version": "0.16.2", "version": "0.16.3",
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.2.tgz", "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.16.3.tgz",
"integrity": "sha512-S+RHW3ogB4nFlajp+Pd+YWuoEJMRBNuFwcke3H/lxlq8ZkX2lEZhvvda+QcwAsQXitmWg3Wi0Oz8fuwXyIKZeg==", "integrity": "sha512-I3kHxt62FMRAX2iVl24iCEtG4UnUInMSbv/LnwevkmjOErLPAQtES4CNzU/fgKRpXYCqp0WWQaRvRYkJhpMIbA==",
"license": "APACHE-2.0", "license": "APACHE-2.0",
"dependencies": { "dependencies": {
"@ungap/structured-clone": "^1.2.0", "@ungap/structured-clone": "^1.2.0",
@@ -3301,9 +3296,9 @@
} }
}, },
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -3594,20 +3589,7 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/string-width/node_modules/ansi-regex": { "node_modules/strip-ansi": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/string-width/node_modules/strip-ansi": {
"version": "7.1.0", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
@@ -3623,19 +3605,6 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1" "url": "https://github.com/chalk/strip-ansi?sponsor=1"
} }
}, },
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-json-comments": { "node_modules/strip-json-comments": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -3787,9 +3756,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.6.2", "version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -3902,6 +3871,16 @@
"xterm": "^5.0.0" "xterm": "^5.0.0"
} }
}, },
"node_modules/xterm-readline/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/xterm-readline/node_modules/emoji-regex": { "node_modules/xterm-readline/node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -3924,6 +3903,19 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/xterm-readline/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yaml": { "node_modules/yaml": {
"version": "1.10.2", "version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@pyscript/core", "name": "@pyscript/core",
"version": "0.6.2", "version": "0.6.7",
"type": "module", "type": "module",
"description": "PyScript", "description": "PyScript",
"module": "./index.js", "module": "./index.js",
@@ -60,8 +60,9 @@
"dependencies": { "dependencies": {
"@ungap/with-resolvers": "^0.1.0", "@ungap/with-resolvers": "^0.1.0",
"@webreflection/idb-map": "^0.3.2", "@webreflection/idb-map": "^0.3.2",
"add-promise-listener": "^0.1.3",
"basic-devtools": "^0.1.6", "basic-devtools": "^0.1.6",
"polyscript": "^0.16.2", "polyscript": "^0.16.3",
"sabayon": "^0.5.2", "sabayon": "^0.5.2",
"sticky-module": "^0.1.1", "sticky-module": "^0.1.1",
"to-json-callback": "^0.1.1", "to-json-callback": "^0.1.1",
@@ -74,13 +75,13 @@
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.34.1", "@codemirror/view": "^6.34.1",
"@playwright/test": "1.45.3", "@playwright/test": "1.45.3",
"@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-terser": "^0.4.4",
"@webreflection/toml-j0.4": "^1.1.3", "@webreflection/toml-j0.4": "^1.1.3",
"@xterm/addon-fit": "^0.10.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0", "@xterm/addon-web-links": "^0.11.0",
"bun": "^1.1.29", "bun": "^1.1.30",
"chokidar": "^4.0.1", "chokidar": "^4.0.1",
"codedent": "^0.1.2", "codedent": "^0.1.2",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
@@ -91,7 +92,7 @@
"rollup-plugin-string": "^3.0.0", "rollup-plugin-string": "^3.0.0",
"static-handler": "^0.5.3", "static-handler": "^0.5.3",
"string-width": "^7.2.0", "string-width": "^7.2.0",
"typescript": "^5.6.2", "typescript": "^5.6.3",
"xterm": "^5.3.0", "xterm": "^5.3.0",
"xterm-readline": "^1.1.1" "xterm-readline": "^1.1.1"
}, },

View File

@@ -146,6 +146,8 @@ for (const [TYPE] of TYPES) {
} }
} else if (!parsed?.plugins?.includes(`!${key}`)) { } else if (!parsed?.plugins?.includes(`!${key}`)) {
toBeAwaited.push(value().then(({ default: p }) => p)); toBeAwaited.push(value().then(({ default: p }) => p));
} else if (key === "error") {
toBeAwaited.push(value().then(({ notOnDOM }) => notOnDOM()));
} }
} }

View File

@@ -1,64 +1,113 @@
import addPromiseListener from "add-promise-listener";
import { assign, dedent } from "polyscript/exports"; import { assign, dedent } from "polyscript/exports";
const { stringify } = JSON;
const invoke = (name, args) => `${name}(code, ${args.join(", ")})`; const invoke = (name, args) => `${name}(code, ${args.join(", ")})`;
export default (options = {}) => { const donkey = ({ type = "py", persistent, terminal, config }) => {
const type = options.type || "py"; const args = persistent ? ["globals()", "__locals__"] : ["{}", "{}"];
const args = options.persistent
? ["globals()", "__locals__"]
: ["{}", "{}"];
const src = URL.createObjectURL( const src = URL.createObjectURL(
new Blob([ new Blob([
dedent(` [
from pyscript import sync, config // this array is to better minify this code once in production
__message__ = lambda e,v: f"\x1b[31m\x1b[1m{e.__name__}\x1b[0m: {v}" "from pyscript import sync, config",
__locals__ = {} '__message__ = lambda e,v: f"\x1b[31m\x1b[1m{e.__name__}\x1b[0m: {v}"',
if config["type"] == "py": "__locals__ = {}",
import sys 'if config["type"] == "py":',
def __error__(_): " import sys",
info = sys.exc_info() " def __error__(_):",
return __message__(info[0], info[1]) " info = sys.exc_info()",
else: " return __message__(info[0], info[1])",
__error__ = lambda e: __message__(e.__class__, e.value) "else:",
def execute(code): " __error__ = lambda e: __message__(e.__class__, e.value)",
try: return ${invoke("exec", args)}; "def execute(code):",
except Exception as e: print(__error__(e)); ` try: return ${invoke("exec", args)};`,
def evaluate(code): " except Exception as e: print(__error__(e));",
try: return ${invoke("eval", args)}; "def evaluate(code):",
except Exception as e: print(__error__(e)); ` try: return ${invoke("eval", args)};`,
sync.execute = execute " except Exception as e: print(__error__(e));",
sync.evaluate = evaluate "sync.execute = execute",
`), "sync.evaluate = evaluate",
].join("\n"),
]), ]),
); );
// create the script that exposes the code to execute or evaluate
const script = assign(document.createElement("script"), { type, src }); const script = assign(document.createElement("script"), { type, src });
script.toggleAttribute("worker", true); script.toggleAttribute("worker", true);
script.toggleAttribute("terminal", true); script.toggleAttribute("terminal", true);
if (options.terminal) script.setAttribute("target", options.terminal); if (terminal) script.setAttribute("target", terminal);
if (options.config) if (config) {
script.setAttribute("config", JSON.stringify(options.config)); script.setAttribute(
"config",
typeof config === "string" ? config : stringify(config),
);
}
return new Promise((resolve) => { return addPromiseListener(
script.addEventListener(`${type}:done`, (event) => { document.body.appendChild(script),
event.stopPropagation(); `${type}:done`,
URL.revokeObjectURL(src); { stopPropagation: true },
const { xworker, process, terminal } = script; ).then(() => {
const { execute, evaluate } = xworker.sync; URL.revokeObjectURL(src);
script.remove(); return script;
resolve({
process,
execute: (code) => execute(dedent(code)),
evaluate: (code) => evaluate(dedent(code)),
clear: () => terminal.clear(),
reset: () => terminal.reset(),
kill: () => {
xworker.terminate();
terminal.dispose();
},
});
});
document.body.append(script);
}); });
}; };
const utils = async (options) => {
const script = await donkey(options);
const { xworker, process, terminal } = script;
const { execute, evaluate } = xworker.sync;
script.remove();
return {
xworker,
process,
terminal,
execute,
evaluate,
};
};
export default async (options = {}) => {
let farmer = await utils(options);
let working = false;
const kill = () => {
if (farmer) {
farmer.xworker.terminate();
farmer.terminal.dispose();
farmer = null;
}
working = false;
};
const reload = async () => {
kill();
farmer = await utils(options);
};
const asyncTask = (method) => async (code) => {
// race condition ... a new task has been
// assigned while the previous one didn't finish
if (working) await reload();
working = true;
try {
return await farmer[method](dedent(code));
} catch (e) {
console.error(e);
} finally {
working = false;
}
};
const asyncMethod = (method) => async () => {
if (working) await reload();
else farmer?.terminal[method]();
};
return {
process: asyncTask("process"),
execute: asyncTask("execute"),
evaluate: asyncTask("evaluate"),
clear: asyncMethod("clear"),
reset: asyncMethod("reset"),
kill,
};
};

View File

@@ -1,6 +1,12 @@
// PyScript Error Plugin // PyScript Error Plugin
import { buffered } from "polyscript/exports";
import { hooks } from "../core.js"; import { hooks } from "../core.js";
let dontBotherDOM = false;
export function notOnDOM() {
dontBotherDOM = true;
}
hooks.main.onReady.add(function override(pyScript) { hooks.main.onReady.add(function override(pyScript) {
// be sure this override happens only once // be sure this override happens only once
hooks.main.onReady.delete(override); hooks.main.onReady.delete(override);
@@ -8,13 +14,15 @@ hooks.main.onReady.add(function override(pyScript) {
// trap generic `stderr` to propagate to it regardless // trap generic `stderr` to propagate to it regardless
const { stderr } = pyScript.io; const { stderr } = pyScript.io;
// override it with our own logic const cb = (error, ...rest) => {
pyScript.io.stderr = (error, ...rest) => {
notify(error.message || error); notify(error.message || error);
// let other plugins or stderr hook, if any, do the rest // let other plugins or stderr hook, if any, do the rest
return stderr(error, ...rest); return stderr(error, ...rest);
}; };
// override it with our own logic
pyScript.io.stderr = pyScript.type === "py" ? cb : buffered(cb);
// be sure uncaught Python errors are also visible // be sure uncaught Python errors are also visible
addEventListener("error", ({ message }) => { addEventListener("error", ({ message }) => {
if (message.startsWith("Uncaught PythonError")) notify(message); if (message.startsWith("Uncaught PythonError")) notify(message);
@@ -30,6 +38,7 @@ hooks.main.onReady.add(function override(pyScript) {
* @param {string} message * @param {string} message
*/ */
export function notify(message) { export function notify(message) {
if (dontBotherDOM) return;
const div = document.createElement("div"); const div = document.createElement("div");
div.className = "py-error"; div.className = "py-error";
div.textContent = message; div.textContent = message;

View File

@@ -77,6 +77,19 @@ async function execute({ currentTarget }) {
const xworker = XWorker.call(new Hook(null, hooks), srcLink, details); const xworker = XWorker.call(new Hook(null, hooks), srcLink, details);
// expose xworker like in terminal or other workers to allow
// creation and destruction of editors on the fly
if (hasRunButton) {
for (const type of TYPES.keys()) {
const editor = currentTarget.closest(`.${type}-editor-box`);
const script = editor?.parentNode?.previousElementSibling;
if (script) {
defineProperties(script, { xworker: { value: xworker } });
break;
}
}
}
const { sync } = xworker; const { sync } = xworker;
const { promise, resolve } = Promise.withResolvers(); const { promise, resolve } = Promise.withResolvers();
envs.set(env, promise); envs.set(env, promise);

View File

@@ -1,9 +1,9 @@
// ⚠️ This file is an artifact: DO NOT MODIFY // ⚠️ This file is an artifact: DO NOT MODIFY
export default { export default {
"pyscript": { "pyscript": {
"__init__.py": "from polyscript import lazy_py_modules as py_import\nfrom pyscript.display import HTML,display\nfrom pyscript.fetch import fetch\nfrom pyscript.magic_js import RUNNING_IN_WORKER,PyWorker,config,current_target,document,js_import,js_modules,sync,window\nfrom pyscript.storage import Storage,storage\nfrom pyscript.websocket import WebSocket\nif not RUNNING_IN_WORKER:from pyscript.workers import create_named_worker,workers\ntry:from pyscript.event_handling import when\nexcept:from pyscript.util import NotSupported;when=NotSupported('pyscript.when','pyscript.when currently not available with this interpreter')", "__init__.py": "from polyscript import lazy_py_modules as py_import\nfrom pyscript.event_handling import when\nfrom pyscript.display import HTML,display\nfrom pyscript.fetch import fetch\nfrom pyscript.magic_js import RUNNING_IN_WORKER,PyWorker,config,current_target,document,js_import,js_modules,sync,window\nfrom pyscript.storage import Storage,storage\nfrom pyscript.websocket import WebSocket\nif not RUNNING_IN_WORKER:from pyscript.workers import create_named_worker,workers",
"display.py": "_L='_repr_mimebundle_'\n_K='image/svg+xml'\n_J='application/json'\n_I='__repr__'\n_H='savefig'\n_G='text/html'\n_F='image/jpeg'\n_E='application/javascript'\n_D='utf-8'\n_C='text/plain'\n_B='image/png'\n_A=None\nimport base64,html,io,re\nfrom pyscript.magic_js import current_target,document,window\n_MIME_METHODS={_H:_B,'_repr_javascript_':_E,'_repr_json_':_J,'_repr_latex':'text/latex','_repr_png_':_B,'_repr_jpeg_':_F,'_repr_pdf_':'application/pdf','_repr_svg_':_K,'_repr_markdown_':'text/markdown','_repr_html_':_G,_I:_C}\ndef _render_image(mime,value,meta):\n\tA=value\n\tif isinstance(A,bytes):A=base64.b64encode(A).decode(_D)\n\tB=re.compile('^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$')\n\tif len(A)>0 and not B.match(A):A=base64.b64encode(A.encode(_D)).decode(_D)\n\tC=f\"data:{mime};charset=utf-8;base64,{A}\";D=' '.join(['{k}=\"{v}\"'for(A,B)in meta.items()]);return f'<img src=\"{C}\" {D}></img>'\ndef _identity(value,meta):return value\n_MIME_RENDERERS={_C:html.escape,_G:_identity,_B:lambda value,meta:_render_image(_B,value,meta),_F:lambda value,meta:_render_image(_F,value,meta),_K:_identity,_J:_identity,_E:lambda value,meta:f\"<script>{value}<\\\\/script>\"}\nclass HTML:\n\tdef __init__(A,html):A._html=html\n\tdef _repr_html_(A):return A._html\ndef _eval_formatter(obj,print_method):\n\tB=obj;A=print_method\n\tif A==_I:return repr(B)\n\telif hasattr(B,A):\n\t\tif A==_H:C=io.BytesIO();B.savefig(C,format='png');C.seek(0);return base64.b64encode(C.read()).decode(_D)\n\t\treturn getattr(B,A)()\n\telif A==_L:return{},{}\ndef _format_mime(obj):\n\tC=obj\n\tif isinstance(C,str):return html.escape(C),_C\n\tD=_eval_formatter(C,_L)\n\tif isinstance(D,tuple):E,I=D\n\telse:E=D\n\tA,F=_A,[]\n\tfor(H,B)in _MIME_METHODS.items():\n\t\tif B in E:A=E[B]\n\t\telse:A=_eval_formatter(C,H)\n\t\tif A is _A:continue\n\t\telif B not in _MIME_RENDERERS:F.append(B);continue\n\t\tbreak\n\tif A is _A:\n\t\tif F:window.console.warn(f\"Rendered object requested unavailable MIME renderers: {F}\")\n\t\tA=repr(A);B=_C\n\telif isinstance(A,tuple):A,G=A\n\telse:G={}\n\treturn _MIME_RENDERERS[B](A,G),B\ndef _write(element,value,append=False):\n\tB=element;C,D=_format_mime(value)\n\tif C=='\\\\n':return\n\tif append:A=document.createElement('div');B.append(A)\n\telse:\n\t\tA=B.lastElementChild\n\t\tif A is _A:A=B\n\tif D in(_E,_G):E=document.createRange().createContextualFragment(C);A.append(E)\n\telse:A.innerHTML=C\ndef display(*D,target=_A,append=True):\n\tC=append;A=target\n\tif A is _A:A=current_target()\n\telif not isinstance(A,str):raise TypeError(f\"target must be str or None, not {A.__class__.__name__}\")\n\telif A=='':raise ValueError('Cannot have an empty target')\n\telif A.startswith('#'):A=A[1:]\n\tB=document.getElementById(A)\n\tif B is _A:raise ValueError(f\"Invalid selector with id={A}. Cannot be found in the page.\")\n\tif B.tagName=='SCRIPT'and hasattr(B,'target'):B=B.target\n\tfor E in D:\n\t\tif not C:B.replaceChildren()\n\t\t_write(B,E,append=C)", "display.py": "_L='_repr_mimebundle_'\n_K='image/svg+xml'\n_J='application/json'\n_I='__repr__'\n_H='savefig'\n_G='text/html'\n_F='image/jpeg'\n_E='application/javascript'\n_D='utf-8'\n_C='text/plain'\n_B='image/png'\n_A=None\nimport base64,html,io,re\nfrom pyscript.magic_js import current_target,document,window\n_MIME_METHODS={_H:_B,'_repr_javascript_':_E,'_repr_json_':_J,'_repr_latex':'text/latex','_repr_png_':_B,'_repr_jpeg_':_F,'_repr_pdf_':'application/pdf','_repr_svg_':_K,'_repr_markdown_':'text/markdown','_repr_html_':_G,_I:_C}\ndef _render_image(mime,value,meta):\n\tA=value\n\tif isinstance(A,bytes):A=base64.b64encode(A).decode(_D)\n\tB=re.compile('^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$')\n\tif len(A)>0 and not B.match(A):A=base64.b64encode(A.encode(_D)).decode(_D)\n\tC=f\"data:{mime};charset=utf-8;base64,{A}\";D=' '.join(['{k}=\"{v}\"'for(A,B)in meta.items()]);return f'<img src=\"{C}\" {D}></img>'\ndef _identity(value,meta):return value\n_MIME_RENDERERS={_C:html.escape,_G:_identity,_B:lambda value,meta:_render_image(_B,value,meta),_F:lambda value,meta:_render_image(_F,value,meta),_K:_identity,_J:_identity,_E:lambda value,meta:f\"<script>{value}<\\\\/script>\"}\nclass HTML:\n\tdef __init__(A,html):A._html=html\n\tdef _repr_html_(A):return A._html\ndef _eval_formatter(obj,print_method):\n\tB=obj;A=print_method\n\tif A==_I:return repr(B)\n\telif hasattr(B,A):\n\t\tif A==_H:C=io.BytesIO();B.savefig(C,format='png');C.seek(0);return base64.b64encode(C.read()).decode(_D)\n\t\treturn getattr(B,A)()\n\telif A==_L:return{},{}\ndef _format_mime(obj):\n\tC=obj\n\tif isinstance(C,str):return html.escape(C),_C\n\tD=_eval_formatter(C,_L)\n\tif isinstance(D,tuple):E,I=D\n\telse:E=D\n\tA,F=_A,[]\n\tfor(H,B)in _MIME_METHODS.items():\n\t\tif B in E:A=E[B]\n\t\telse:A=_eval_formatter(C,H)\n\t\tif A is _A:continue\n\t\telif B not in _MIME_RENDERERS:F.append(B);continue\n\t\tbreak\n\tif A is _A:\n\t\tif F:window.console.warn(f\"Rendered object requested unavailable MIME renderers: {F}\")\n\t\tA=repr(A);B=_C\n\telif isinstance(A,tuple):A,G=A\n\telse:G={}\n\treturn _MIME_RENDERERS[B](A,G),B\ndef _write(element,value,append=False):\n\tB=element;C,D=_format_mime(value)\n\tif C=='\\\\n':return\n\tif append:A=document.createElement('div');B.append(A)\n\telse:\n\t\tA=B.lastElementChild\n\t\tif A is _A:A=B\n\tif D in(_E,_G):E=document.createRange().createContextualFragment(C);A.append(E)\n\telse:A.innerHTML=C\ndef display(*D,target=_A,append=True):\n\tC=append;A=target\n\tif A is _A:A=current_target()\n\telif not isinstance(A,str):raise TypeError(f\"target must be str or None, not {A.__class__.__name__}\")\n\telif A=='':raise ValueError('Cannot have an empty target')\n\telif A.startswith('#'):A=A[1:]\n\tB=document.getElementById(A)\n\tif B is _A:raise ValueError(f\"Invalid selector with id={A}. Cannot be found in the page.\")\n\tif B.tagName=='SCRIPT'and hasattr(B,'target'):B=B.target\n\tfor E in D:\n\t\tif not C:B.replaceChildren()\n\t\t_write(B,E,append=C)",
"event_handling.py": "import inspect\ntry:from pyodide.ffi.wrappers import add_event_listener\nexcept ImportError:\n\tdef add_event_listener(el,event_type,func):el.addEventListener(event_type,func)\nfrom pyscript.magic_js import document\ndef when(event_type=None,selector=None):\n\tA=selector\n\tdef B(func):\n\t\tB=func;from pyscript.web import Element as E,ElementCollection as F\n\t\tif isinstance(A,str):C=document.querySelectorAll(A)\n\t\telif isinstance(A,E):C=[A._dom_element]\n\t\telif isinstance(A,F):C=[A._dom_element for A in A]\n\t\telif isinstance(A,list):C=A\n\t\telse:C=[A]\n\t\ttry:\n\t\t\tG=inspect.signature(B)\n\t\t\tif not G.parameters:\n\t\t\t\tif inspect.iscoroutinefunction(B):\n\t\t\t\t\tasync def D(*A,**C):await B()\n\t\t\t\telse:\n\t\t\t\t\tdef D(*A,**C):B()\n\t\t\telse:D=B\n\t\texcept AttributeError:\n\t\t\tdef D(*C,**D):\n\t\t\t\ttry:return B(*C,**D)\n\t\t\t\texcept TypeError as A:\n\t\t\t\t\tif'takes'in str(A)and'positional arguments'in str(A):return B()\n\t\t\t\t\traise\n\t\tfor H in C:add_event_listener(H,event_type,D)\n\t\treturn B\n\treturn B", "event_handling.py": "import inspect\ntry:from pyodide.ffi.wrappers import add_event_listener\nexcept ImportError:\n\tdef add_event_listener(el,event_type,func):el.addEventListener(event_type,func)\nfrom pyscript.magic_js import document\ndef when(target,*B,**C):\n\tI='handler';F=target;E=None\n\tif B and callable(B[0]):E=B[0];B=B[1:]\n\telif callable(C.get(I)):E=C.pop(I)\n\tG=hasattr(F,'__when__')\n\tif not G:\n\t\tif B:A=B[0]\n\t\telif C:A=C.get('selector')\n\t\tif not A:raise ValueError('No selector provided.')\n\t\tfrom pyscript.web import Element as J,ElementCollection as K\n\t\tif isinstance(A,str):D=document.querySelectorAll(A)\n\t\telif isinstance(A,J):D=[A._dom_element]\n\t\telif isinstance(A,K):D=[A._dom_element for A in A]\n\t\telif isinstance(A,list):D=A\n\t\telse:D=[A]\n\tdef H(func):\n\t\tA=func\n\t\ttry:\n\t\t\tH=inspect.signature(A)\n\t\t\tif not H.parameters:\n\t\t\t\tif inspect.iscoroutinefunction(A):\n\t\t\t\t\tasync def E(*B,**C):await A()\n\t\t\t\telse:\n\t\t\t\t\tdef E(*B,**C):A()\n\t\t\telse:E=A\n\t\texcept AttributeError:\n\t\t\tdef E(*C,**D):\n\t\t\t\ttry:return A(*C,**D)\n\t\t\t\texcept TypeError as B:\n\t\t\t\t\tif'takes'in str(B)and'positional arguments'in str(B):return A()\n\t\t\t\t\traise\n\t\tif G:F.__when__(E,*B,**C)\n\t\telse:\n\t\t\tfor I in D:add_event_listener(I,F,E)\n\t\treturn A\n\tif E:H(E)\n\telse:return H",
"fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A", "fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A",
"ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs", "ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs",
"flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A<len(input):C.append(_transform(B,input,input[A]));A+=1\n\treturn _json.dumps(C,*D,**E)", "flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A<len(input):C.append(_transform(B,input,input[A]));A+=1\n\treturn _json.dumps(C,*D,**E)",

View File

@@ -30,6 +30,7 @@
# as it works transparently in both the main thread and worker cases. # as it works transparently in both the main thread and worker cases.
from polyscript import lazy_py_modules as py_import from polyscript import lazy_py_modules as py_import
from pyscript.event_handling import when
from pyscript.display import HTML, display from pyscript.display import HTML, display
from pyscript.fetch import fetch from pyscript.fetch import fetch
from pyscript.magic_js import ( from pyscript.magic_js import (
@@ -48,14 +49,3 @@ from pyscript.websocket import WebSocket
if not RUNNING_IN_WORKER: if not RUNNING_IN_WORKER:
from pyscript.workers import create_named_worker, workers from pyscript.workers import create_named_worker, workers
try:
from pyscript.event_handling import when
except:
# TODO: should we remove this? Or at the very least, we should capture
# the traceback otherwise it's very hard to debug
from pyscript.util import NotSupported
when = NotSupported(
"pyscript.when", "pyscript.when currently not available with this interpreter"
)

View File

@@ -12,14 +12,42 @@ except ImportError:
from pyscript.magic_js import document from pyscript.magic_js import document
def when(event_type=None, selector=None): def when(target, *args, **kwargs):
""" """
Decorates a function and passes py-* events to the decorated function A decorator and function for attaching event handlers to DOM elements or
The events might or not be an argument of the decorated function whenable objects.
When used as a decorator, the target is the object that will trigger the
event. The handler function is the decorated function. The handler function
will be called when the target is triggered.
When used as a function, the target is the object that will trigger the
event. The handler function is the next argument. The remaining arguments
and keyword arguments are passed to the target when it is triggered.
""" """
# If "when" is called as a function, try to grab the handler from the
# arguments. If there's no handler, this must be a decorator based call.
handler = None
if args and callable(args[0]):
handler = args[0]
args = args[1:]
elif callable(kwargs.get("handler")):
handler = kwargs.pop("handler")
def decorator(func): # Does the target implement the when protocol?
whenable = hasattr(target, "__when__")
# If not when-able, the DOM selector for the target event.
if not whenable:
# The target is an event linked to a DOM selector. Extract the
# selector from the arguments or keyword arguments.
if args:
selector = args[0]
elif kwargs:
selector = kwargs.get("selector")
if not selector:
# There must be a selector if the target is not when-able.
raise ValueError("No selector provided.")
# Grab the DOM elements to which the target event will be attached.
from pyscript.web import Element, ElementCollection from pyscript.web import Element, ElementCollection
if isinstance(selector, str): if isinstance(selector, str):
@@ -36,11 +64,11 @@ def when(event_type=None, selector=None):
else: else:
elements = [selector] elements = [selector]
def decorator(func):
try: try:
sig = inspect.signature(func) sig = inspect.signature(func)
# Function doesn't receive events # Function doesn't receive events
if not sig.parameters: if not sig.parameters:
# Function is async: must be awaited # Function is async: must be awaited
if inspect.iscoroutinefunction(func): if inspect.iscoroutinefunction(func):
@@ -54,7 +82,6 @@ def when(event_type=None, selector=None):
else: else:
wrapper = func wrapper = func
except AttributeError: except AttributeError:
# TODO: this is very ugly hack to get micropython working because inspect.signature # TODO: this is very ugly hack to get micropython working because inspect.signature
# doesn't exist, but we need to actually properly replace inspect.signature. # doesn't exist, but we need to actually properly replace inspect.signature.
@@ -68,9 +95,15 @@ def when(event_type=None, selector=None):
raise raise
for el in elements: if whenable:
add_event_listener(el, event_type, wrapper) target.__when__(wrapper, *args, **kwargs)
else:
for el in elements:
add_event_listener(el, target, wrapper)
return func return func
return decorator if handler:
decorator(handler)
else:
return decorator

View File

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

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module" src="../../dist/core.js"></script>
<script type="mpy">
from pyscript import document
import sys
print("This is an error", file=sys.stderr)
document.documentElement.classList.add("ok");
</script>
</head>
</html>

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module" src="../../dist/core.js"></script>
<script type="mpy" config="mpy-no-error.toml">
from pyscript import document
import sys
print("This is an error", file=sys.stderr)
document.documentElement.classList.add("ok");
</script>
</head>
</html>

View File

@@ -0,0 +1 @@
plugins = ["!error"]

View File

@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<title>mpy using py named worker</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script type="module" src="../../../../dist/core.js"></script>
</head>
<body>
<script type="mpy" config="../config.toml">
from pyscript import create_named_worker
await create_named_worker("../worker.py", name="pyodide_version", type="py")
from test import test
await test("pyodide_version")
</script>
</body>
</html>

View File

@@ -1,30 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script type="module" src="../../../dist/core.js"></script>
</head>
<body>
<script type="mpy" async>
from pyscript import create_named_worker
await create_named_worker("./worker.py", name="micropython_version", type="mpy")
</script>
<script type="mpy" config="./config.toml" async>
from test import test
await test("mpy")
</script>
<script type="py" config="./config.toml" async>
from test import test
await test("py")
</script>
<script type="py" name="pyodide_version" worker>
def pyodide_version():
import sys
return sys.version
__export__ = ['pyodide_version']
</script>
</body>
</html>

View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<title>mpy using py named worker</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script type="module" src="../../../../dist/core.js"></script>
</head>
<body>
<script type="mpy" config="../config.toml">
from test import test
await test("pyodide_version")
</script>
<script type="py" src="../worker.py" name="pyodide_version" worker></script>
</body>
</html>

View File

@@ -1,29 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>named workers</title>
<script type="module" src="../../../dist/core.js"></script>
</head>
<body>
<script type="mpy" async>
from pyscript import workers
await (await workers["mpy"]).greetings()
await (await workers["py"]).greetings()
</script>
<script type="mpy" worker name="mpy">
def greetings():
print("micropython")
__export__ = ['greetings']
</script>
<script type="py" worker name="py">
def greetings():
print("pyodide")
__export__ = ['greetings']
</script>
</body>
</html>

View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<title>py using mpy named worker</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script type="module" src="../../../../dist/core.js"></script>
</head>
<body>
<script type="py" config="../config.toml">
from test import test
await test("micropython_version")
</script>
<script type="mpy" src="../worker.py" name="micropython_version" worker></script>
</body>
</html>

View File

@@ -1,19 +1,13 @@
from pyscript import document, workers from pyscript import document, workers
async def test(interpreter): async def test(name):
# accessed as item # retrieve sync utilities from the named worker
named = await workers.micropython_version named = await workers[name]
version = await named.micropython_version() # invoke the runtime_version __export__ + show it
version = await named.runtime_version()
document.body.append(version) document.body.append(version)
document.body.append(document.createElement("hr"))
# accessed as attribute # flag the expectations around name done
named = await workers["pyodide_version"] document.documentElement.classList.add(name)
version = await named.pyodide_version()
document.body.append(version)
document.body.append(document.createElement("hr"))
document.documentElement.classList.add(interpreter)

View File

@@ -1,7 +1,7 @@
def micropython_version(): def runtime_version():
import sys import sys
return sys.version return sys.version
__export__ = ["micropython_version"] __export__ = ['runtime_version']

View File

@@ -95,9 +95,19 @@ test('MicroPython + JS Storage', async ({ page }) => {
await page.waitForSelector('html.ok'); await page.waitForSelector('html.ok');
}); });
test('MicroPython + workers', async ({ page }) => { test('MicroPython using named Pyodide Worker', async ({ page }) => {
await page.goto('http://localhost:8080/tests/javascript/workers/index.html'); await page.goto('http://localhost:8080/tests/javascript/workers/mpy/index.html');
await page.waitForSelector('html.mpy.py'); await page.waitForSelector('html.pyodide_version');
});
test('MicroPython creating a named Pyodide Worker', async ({ page }) => {
await page.goto('http://localhost:8080/tests/javascript/workers/create_named/index.html');
await page.waitForSelector('html.pyodide_version');
});
test('Pyodide using named MicroPython Worker', async ({ page }) => {
await page.goto('http://localhost:8080/tests/javascript/workers/py/index.html');
await page.waitForSelector('html.micropython_version');
}); });
test('MicroPython Editor setup error', async ({ page }) => { test('MicroPython Editor setup error', async ({ page }) => {
@@ -128,3 +138,17 @@ test('Pyodide lockFileURL vs CDN', async ({ page }) => {
const body = await page.evaluate(() => document.body.textContent); const body = await page.evaluate(() => document.body.textContent);
await expect(body).toBe('OK'); await expect(body).toBe('OK');
}); });
test('MicroPython buffered error', async ({ page }) => {
await page.goto('http://localhost:8080/tests/javascript/mpy-error.html');
await page.waitForSelector('html.ok');
const body = await page.evaluate(() => document.body.textContent.trim());
await expect(body).toBe('This is an error');
});
test('MicroPython buffered NO error', async ({ page }) => {
await page.goto('http://localhost:8080/tests/javascript/mpy-no-error.html');
await page.waitForSelector('html.ok');
const body = await page.evaluate(() => document.body.textContent.trim());
await expect(body).toBe('');
});

View File

@@ -12,13 +12,23 @@ const {
kill, kill,
} = await donkey({ terminal: '#container' }); } = await donkey({ terminal: '#container' });
clearButton.onclick = clear; clearButton.onclick = async () => {
killButton.onclick = kill; killButton.disabled = true;
clearButton.disabled = true;
await clear();
runButton.disabled = false;
};
killButton.onclick = () => {
killButton.disabled = true;
clearButton.disabled = true;
runButton.disabled = true;
kill();
};
runButton.disabled = false; runButton.disabled = false;
runButton.onclick = async () => { runButton.onclick = async () => {
killButton.disabled = false; killButton.disabled = false;
clearButton.disabled = true; clearButton.disabled = false;
runButton.disabled = true; runButton.disabled = true;
// multiline code // multiline code
await execute(` await execute(`
@@ -29,6 +39,5 @@ runButton.onclick = async () => {
const name = await evaluate('input("what is your name? ")'); const name = await evaluate('input("what is your name? ")');
alert(`Hello ${name}`); alert(`Hello ${name}`);
killButton.disabled = true; killButton.disabled = true;
clearButton.disabled = false;
runButton.disabled = false; runButton.disabled = false;
}; };

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PyScript Error Bug?</title>
<link rel="stylesheet" href="../../../dist/core.css">
<script type="module" src="../../../dist/core.js"></script>
</head>
<body>
<py-config>
plugins = ["!error"]
</py-config>
<script type="py">
import sys
print("This is normal content")
print("This is error content", file=sys.stderr)
</script>
<!-- Attempt 2; inline config
<script type="py" config='plugins=["!error"]'>
import sys
print("This is normal content")
print("This is error content", file=sys.stderr)
</script> -->
<!-- Attempt 3; external pyscript.toml
<script type="py" config="pyscript.toml">
import sys
print("This is normal content")
print("This is error content", file=sys.stderr)
</script> -->
<!-- Attempt 4; micropython
<script type="mpy">
import sys
print("This is normal content")
print("This is error content", file=sys.stderr)
</script> -->
<div id="result"></div>
</body>
</html>

View File

@@ -0,0 +1 @@
plugins = ["!error"]

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyTerminal</title> <title>PyEditor Failure</title>
<link rel="stylesheet" href="../../dist/core.css"> <link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script> <script type="module" src="../../dist/core.js"></script>
</head> </head>

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyTerminal</title> <title>PyEditor</title>
<link rel="stylesheet" href="../../dist/core.css"> <link rel="stylesheet" href="../../dist/core.css">
<script type="module" src="../../dist/core.js"></script> <script type="module" src="../../dist/core.js"></script>
</head> </head>

View File

@@ -214,3 +214,130 @@ def test_when_decorator_invalid_selector():
def foo(evt): ... def foo(evt): ...
assert "'#.bad' is not a valid selector" in str(e.exception), str(e.exception) assert "'#.bad' is not a valid selector" in str(e.exception), str(e.exception)
def test_when_decorates_a_whenable():
"""
When the @when decorator is used on a function to handle a whenable object,
the function should be called when the whenable object is triggered.
"""
class MyWhenable:
"""
A simple whenable object that can be triggered.
"""
def __init__(self):
self.handler = None
self.args = None
self.kwargs = None
def trigger(self):
"""
Triggers the whenable object, resulting in the handler being
called.
"""
if self.handler:
result = {
"args": self.args,
"kwargs": self.kwargs,
}
self.handler(result) # call the handler
def __when__(self, handler, *args, **kwargs):
"""
These implementation details depend on the sort of thing the
whenable object represents. This is just a simple example.
"""
self.handler = handler
self.args = args
self.kwargs = kwargs
whenable = MyWhenable()
counter = 0
# When as a decorator.
@web.when(whenable, "foo", "bar", baz="qux")
def foo(result):
"""
A function that should be called when the whenable object is triggered.
The result generated by the whenable object should be passed to the
function.
"""
nonlocal counter
counter += 1
assert result["args"] == ("foo", "bar")
assert result["kwargs"] == {"baz": "qux"}
# The function should not be called until the whenable object is triggered.
assert counter == 0
# Trigger the whenable object.
whenable.trigger()
# The function should have been called when the whenable object was
# triggered.
assert counter == 1
def test_when_called_with_a_whenable():
"""
The when function should be able to be called with a whenable object,
a handler function, and arguments.
"""
class MyWhenable:
"""
A simple whenable object that can be triggered.
"""
def __init__(self):
self.handler = None
self.args = None
self.kwargs = None
def trigger(self):
"""
Triggers the whenable object, resulting in the handler being
called.
"""
if self.handler:
result = {
"args": self.args,
"kwargs": self.kwargs,
}
self.handler(result) # call the handler
def __when__(self, handler, *args, **kwargs):
"""
These implementation details depend on the sort of thing the
whenable object represents. This is just a simple example.
"""
self.handler = handler
self.args = args
self.kwargs = kwargs
whenable = MyWhenable()
counter = 0
def handler(result):
"""
A function that should be called when the whenable object is triggered.
The result generated by the whenable object should be passed to the
function.
"""
nonlocal counter
counter += 1
assert result["args"] == ("foo", "bar")
assert result["kwargs"] == {"baz": "qux"}
# When as a function.
web.when(whenable, handler, "foo", "bar", baz="qux")
# The function should not be called until the whenable object is triggered.
assert counter == 0
# Trigger the whenable object.
whenable.trigger()
# The function should have been called when the whenable object was
# triggered.
assert counter == 1

View File

@@ -1,4 +1,11 @@
export function donkey(options: any): Promise<any>; export function donkey(options: any): Promise<{
process: (code: any) => Promise<any>;
execute: (code: any) => Promise<any>;
evaluate: (code: any) => Promise<any>;
clear: () => Promise<void>;
reset: () => Promise<void>;
kill: () => void;
}>;
export function offline_interpreter(config: any): string; export function offline_interpreter(config: any): string;
import { stdlib } from "./stdlib.js"; import { stdlib } from "./stdlib.js";
import { optional } from "./stdlib.js"; import { optional } from "./stdlib.js";

View File

@@ -1,2 +1,9 @@
declare function _default(options?: {}): Promise<any>; declare function _default(options?: {}): Promise<{
process: (code: any) => Promise<any>;
execute: (code: any) => Promise<any>;
evaluate: (code: any) => Promise<any>;
clear: () => Promise<void>;
reset: () => Promise<void>;
kill: () => void;
}>;
export default _default; export default _default;

View File

@@ -1,3 +1,4 @@
export function notOnDOM(): void;
/** /**
* Add a banner to the top of the page, notifying the user of an error * Add a banner to the top of the page, notifying the user of an error
* @param {string} message * @param {string} message

View File

@@ -1,4 +1,4 @@
black==24.8.0 black==24.10.0
pre-commit==3.7.1 pre-commit==3.7.1
python-minifier==2.11.0 python-minifier==2.11.0
setuptools==72.1.0 setuptools==72.1.0