mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
Moves Python code out of interpreter file (#207)
* make copy of .py files part of build process * move code out ofinterpreter file and make it download and load code during initialization * fix double ; in interpreter * remove debugging print * update dependencies * fix project name and version * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * change fmt-py * lint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove extra content * define missing strict type * create build folder if doesn't exist Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -56,7 +56,7 @@ fmt: fmt-py fmt-ts
|
||||
@echo "Format completed"
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: fmt-ts-check fmt-py
|
||||
fmt-check: fmt-ts-check fmt-py-check
|
||||
@echo "Format check completed"
|
||||
|
||||
.PHONY: fmt-ts
|
||||
@@ -69,7 +69,8 @@ fmt-ts-check:
|
||||
|
||||
.PHONY: fmt-py
|
||||
fmt-py:
|
||||
$(conda_run) black -l 88 .
|
||||
$(conda_run) black --skip-string-normalization .
|
||||
$(conda_run) isort --profile black .
|
||||
|
||||
.PHONY: fmt-py-check
|
||||
fmt-py-check:
|
||||
|
||||
@@ -9,3 +9,4 @@ dependencies:
|
||||
- black
|
||||
- isort
|
||||
- codespell
|
||||
- pre-commit
|
||||
|
||||
@@ -153,7 +153,6 @@ def micrograd_demo(*args, **kwargs):
|
||||
plt.ylim(yy.min(), yy.max())
|
||||
|
||||
finish = datetime.datetime.now()
|
||||
# print(f"It took {(finish-start).seconds} seconds to run this code.")
|
||||
print_div(f"It took {(finish-start).seconds} seconds to run this code.")
|
||||
|
||||
plt
|
||||
|
||||
102
pyscriptjs/package-lock.json
generated
102
pyscriptjs/package-lock.json
generated
@@ -145,9 +145,9 @@
|
||||
}
|
||||
},
|
||||
"@codemirror/lang-python": {
|
||||
"version": "0.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-0.19.4.tgz",
|
||||
"integrity": "sha512-eSH1JXbf0D30y+f3nWy/+bTLAIV8RmcQbbVD8DsBxkxOHMVKcILgxFRHCovba8YEMtmq45I1DoWcNt1CeKnrYQ==",
|
||||
"version": "0.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-0.19.5.tgz",
|
||||
"integrity": "sha512-MQf7t0k6+i9KCzlFCI8EY+jjwyXLy5AwjmXsMyMCMbOw/97j70jFZYrs7Mm7RJakNE2rypWhnLGlyBTSYMqR5g==",
|
||||
"requires": {
|
||||
"@codemirror/highlight": "^0.19.7",
|
||||
"@codemirror/language": "^0.19.0",
|
||||
@@ -404,9 +404,9 @@
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-typescript": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.1.tgz",
|
||||
"integrity": "sha512-84rExe3ICUBXzqNX48WZV2Jp3OddjTMX97O2Py6D1KJaGSwWp0mDHXj+bCGNJqWHIEKDIT2U0sDjhP4czKi6cA==",
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.2.tgz",
|
||||
"integrity": "sha512-MtgyR5LNHZr3GyN0tM7gNO9D0CS+Y+vflS4v/PHmrX17JCkHUYKvQ5jN5o3cz1YKllM3duXUqu3yOHwMPUxhDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
@@ -683,13 +683,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "10.4.4",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.4.tgz",
|
||||
"integrity": "sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==",
|
||||
"version": "10.4.7",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz",
|
||||
"integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.20.2",
|
||||
"caniuse-lite": "^1.0.30001317",
|
||||
"browserslist": "^4.20.3",
|
||||
"caniuse-lite": "^1.0.30001335",
|
||||
"fraction.js": "^4.2.0",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
@@ -728,15 +728,15 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.20.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
|
||||
"integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
|
||||
"version": "4.20.3",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
|
||||
"integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001317",
|
||||
"electron-to-chromium": "^1.4.84",
|
||||
"caniuse-lite": "^1.0.30001332",
|
||||
"electron-to-chromium": "^1.4.118",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^2.0.2",
|
||||
"node-releases": "^2.0.3",
|
||||
"picocolors": "^1.0.0"
|
||||
}
|
||||
},
|
||||
@@ -777,9 +777,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001323",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz",
|
||||
"integrity": "sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA==",
|
||||
"version": "1.0.30001335",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz",
|
||||
"integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@@ -810,9 +810,9 @@
|
||||
}
|
||||
},
|
||||
"codemirror": {
|
||||
"version": "5.65.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.2.tgz",
|
||||
"integrity": "sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA=="
|
||||
"version": "5.65.3",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.3.tgz",
|
||||
"integrity": "sha512-kCC0iwGZOVZXHEKW3NDTObvM7pTIyowjty4BUqeREROc/3I6bWbgZDA3fGDwlA+rbgRjvnRnfqs9SfXynel1AQ=="
|
||||
},
|
||||
"color": {
|
||||
"version": "4.2.1",
|
||||
@@ -1019,9 +1019,9 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.103",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz",
|
||||
"integrity": "sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==",
|
||||
"version": "1.4.134",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.134.tgz",
|
||||
"integrity": "sha512-OdD7M2no4Mi8PopfvoOuNcwYDJ2mNFxaBfurA6okG3fLBaMcFah9S+si84FhX+FIWLKkdaiHfl4A+5ep/gOVrg==",
|
||||
"dev": true
|
||||
},
|
||||
"error-ex": {
|
||||
@@ -1832,9 +1832,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz",
|
||||
"integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==",
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
@@ -1853,9 +1853,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
|
||||
"integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz",
|
||||
"integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
@@ -1969,12 +1969,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.12",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz",
|
||||
"integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==",
|
||||
"version": "8.4.13",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz",
|
||||
"integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.1",
|
||||
"nanoid": "^3.3.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
@@ -2179,9 +2179,9 @@
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.70.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz",
|
||||
"integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==",
|
||||
"version": "2.71.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.71.1.tgz",
|
||||
"integrity": "sha512-lMZk3XfUBGjrrZQpvPSoXcZSfKcJ2Bgn+Z0L1MoW2V8Wh7BVM+LOBJTPo16yul2MwL59cXedzW1ruq3rCjSRgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
@@ -2480,9 +2480,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"svelte": {
|
||||
"version": "3.46.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.6.tgz",
|
||||
"integrity": "sha512-o9nNft/OzCz/9kJpmWa1S52GAM+huCjPIsNWydYmgei74ZWlOA9/hN9+Z12INdklghu31seEXZMRHhS1+8DETw=="
|
||||
"version": "3.48.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz",
|
||||
"integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ=="
|
||||
},
|
||||
"svelte-check": {
|
||||
"version": "1.6.0",
|
||||
@@ -2558,9 +2558,9 @@
|
||||
"integrity": "sha512-0bnbMGbsE1LUnlioDcf27tl2O8kjuXlTXMXzIxC7LoIOWmqn0D+zd539HfLiQbdLuOHGTaynwN9V+4ehhEu1Jw=="
|
||||
},
|
||||
"svelte-preprocess": {
|
||||
"version": "4.10.4",
|
||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.4.tgz",
|
||||
"integrity": "sha512-fuwol0N4UoHsNQolLFbMqWivqcJ9N0vfWO9IuPAiX/5okfoGXURyJ6nECbuEIv0nU3M8Xe2I1ONNje2buk7l6A==",
|
||||
"version": "4.10.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.6.tgz",
|
||||
"integrity": "sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/pug": "^2.0.4",
|
||||
@@ -2751,9 +2751,9 @@
|
||||
"integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
|
||||
"dev": true
|
||||
},
|
||||
"tsutils": {
|
||||
@@ -2789,9 +2789,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz",
|
||||
"integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
|
||||
"version": "4.6.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
|
||||
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"name": "pyscript",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
@@ -15,36 +15,36 @@
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"@rollup/plugin-typescript": "^8.1.0",
|
||||
"@rollup/plugin-typescript": "^8.3.2",
|
||||
"@tsconfig/svelte": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"autoprefixer": "^10.2.3",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-plugin-svelte3": "^3.4.1",
|
||||
"postcss": "^8.2.4",
|
||||
"postcss": "^8.4.13",
|
||||
"prettier": "^2.6.2",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup": "^2.71.1",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-serve": "^1.1.0",
|
||||
"rollup-plugin-svelte": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"svelte": "^3.0.0",
|
||||
"svelte": "^3.48.0",
|
||||
"svelte-check": "^1.0.0",
|
||||
"svelte-preprocess": "^4.6.3",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
"tailwindcss": "^2.0.2",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^4.1.3"
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.6.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/basic-setup": "^0.19.1",
|
||||
"@codemirror/lang-python": "^0.19.4",
|
||||
"@codemirror/lang-python": "^0.19.5",
|
||||
"@codemirror/state": "^0.19.9",
|
||||
"@codemirror/theme-one-dark": "^0.19.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.0.0",
|
||||
"codemirror": "^5.65.2",
|
||||
"codemirror": "^5.65.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"sirv-cli": "^1.0.0",
|
||||
"svelte-fa": "^2.4.0",
|
||||
|
||||
@@ -6,7 +6,43 @@ import { terser } from "rollup-plugin-terser";
|
||||
import sveltePreprocess from "svelte-preprocess";
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
import css from "rollup-plugin-css-only";
|
||||
import serve from 'rollup-plugin-serve'
|
||||
import serve from 'rollup-plugin-serve';
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
function copyPythonFiles(from, to, overwrite = false) {
|
||||
return {
|
||||
name: 'copy-files',
|
||||
generateBundle() {
|
||||
const log = msg => console.log('\x1b[36m%s\x1b[0m', msg)
|
||||
log(`copy files: ${from} → ${to}`)
|
||||
|
||||
// create folder if it doesn't exist
|
||||
if (!fs.existsSync(to)){
|
||||
log(`Destination folder ${to} doesn't exist. Creating...`)
|
||||
fs.mkdirSync(to);
|
||||
}
|
||||
|
||||
fs.readdirSync(from).forEach(file => {
|
||||
const fromFile = `${from}/${file}`
|
||||
const toFile = `${to}/${file}`
|
||||
if (fromFile.endsWith(`.py`)){
|
||||
log(`----> ${fromFile} → ${toFile}`)
|
||||
if (fs.existsSync(toFile) && !overwrite){
|
||||
log(`skipping ${fromFile} → ${toFile}`)
|
||||
return
|
||||
}else{
|
||||
fs.copyFileSync(
|
||||
path.resolve(fromFile),
|
||||
path.resolve(toFile)
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH || (process.env.NODE_ENV === "production");
|
||||
|
||||
@@ -68,6 +104,8 @@ export default {
|
||||
sourceMap: !production,
|
||||
inlineSources: !production,
|
||||
}),
|
||||
// Copy all the python files from source to the build folder
|
||||
copyPythonFiles("./src/", "./examples/build", true),
|
||||
!production && serve(),
|
||||
!production && livereload("public"),
|
||||
production && terser(),
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Tailwind from './Tailwind.svelte';
|
||||
import { loadInterpreter } from './interpreter';
|
||||
import {
|
||||
initializers,
|
||||
loadedEnvironments,
|
||||
mode,
|
||||
postInitializers,
|
||||
pyodideLoaded,
|
||||
scriptsQueue,
|
||||
} from './stores';
|
||||
import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue } from './stores';
|
||||
|
||||
let pyodideReadyPromise;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ let environments;
|
||||
let currentMode;
|
||||
let Element;
|
||||
|
||||
|
||||
pyodideLoaded.subscribe(value => {
|
||||
runtime = value;
|
||||
});
|
||||
@@ -14,7 +13,6 @@ loadedEnvironments.subscribe(value => {
|
||||
environments = value;
|
||||
});
|
||||
|
||||
|
||||
mode.subscribe(value => {
|
||||
currentMode = value;
|
||||
});
|
||||
@@ -203,8 +201,8 @@ function createWidget(name: string, code: string, klass: string) {
|
||||
// });
|
||||
// }, 2000);
|
||||
pyodideLoaded.subscribe(value => {
|
||||
console.log("RUNTIME READY", value)
|
||||
if ("runPythonAsync" in value){
|
||||
console.log('RUNTIME READY', value);
|
||||
if ('runPythonAsync' in value) {
|
||||
runtime = value;
|
||||
setTimeout(() => {
|
||||
this.eval(this.code).then(() => {
|
||||
|
||||
@@ -54,9 +54,8 @@ export class PyBox extends HTMLElement {
|
||||
|
||||
this.widths.forEach((width, index) => {
|
||||
const node: ChildNode = mainDiv.childNodes[index];
|
||||
addClasses(node as HTMLElement, [width, 'mx-1'])
|
||||
|
||||
})
|
||||
addClasses(node as HTMLElement, [width, 'mx-1']);
|
||||
});
|
||||
|
||||
this.appendChild(mainDiv);
|
||||
console.log('py-box connected');
|
||||
|
||||
@@ -9,7 +9,7 @@ let runtime;
|
||||
|
||||
pyodideLoaded.subscribe(value => {
|
||||
runtime = value;
|
||||
console.log("RUNTIME READY")
|
||||
console.log('RUNTIME READY');
|
||||
});
|
||||
|
||||
export class PyEnv extends HTMLElement {
|
||||
@@ -33,7 +33,7 @@ export class PyEnv extends HTMLElement {
|
||||
this.innerHTML = '';
|
||||
|
||||
const env = [];
|
||||
const paths = [];
|
||||
const paths: string[] = [];
|
||||
|
||||
this.environment = jsyaml.load(this.code);
|
||||
if (this.environment === undefined) return;
|
||||
@@ -54,8 +54,8 @@ export class PyEnv extends HTMLElement {
|
||||
}
|
||||
|
||||
async function loadPaths() {
|
||||
const pyodide = await pyodideReadyPromise;
|
||||
for (const singleFile of paths) {
|
||||
console.log(`loading ${singleFile}`);
|
||||
await loadFromFile(singleFile, runtime);
|
||||
}
|
||||
console.log('paths loaded');
|
||||
|
||||
@@ -50,7 +50,7 @@ export class PyRepl extends BaseEvalElement {
|
||||
|
||||
// add an extra div where we can attach the codemirror editor
|
||||
this.editorNode = document.createElement('div');
|
||||
addClasses(this.editorNode, ["editor-box", "border", "border-gray-300", "group", "relative"]);
|
||||
addClasses(this.editorNode, ['editor-box', 'border', 'border-gray-300', 'group', 'relative']);
|
||||
this.shadow.appendChild(this.wrapper);
|
||||
}
|
||||
|
||||
@@ -64,16 +64,16 @@ export class PyRepl extends BaseEvalElement {
|
||||
languageConf.of(python()),
|
||||
keymap.of([
|
||||
...defaultKeymap,
|
||||
{ key: "Ctrl-Enter", run: createCmdHandler(this) },
|
||||
{ key: "Shift-Enter", run: createCmdHandler(this) }
|
||||
])
|
||||
{ key: 'Ctrl-Enter', run: createCmdHandler(this) },
|
||||
{ key: 'Shift-Enter', run: createCmdHandler(this) },
|
||||
]),
|
||||
];
|
||||
const customTheme = EditorView.theme({
|
||||
'&.cm-focused .cm-editor': { outline: '0px' },
|
||||
'.cm-scroller': { lineHeight: 2.5 },
|
||||
'.cm-activeLine': { backgroundColor: '#fff' },
|
||||
'.cm-content': { padding: 0, backgroundColor: '#f5f5f5' },
|
||||
'&.cm-focused .cm-content': { border: '1px solid #1876d2' }
|
||||
'&.cm-focused .cm-content': { border: '1px solid #1876d2' },
|
||||
});
|
||||
|
||||
if (!this.hasAttribute('theme')) {
|
||||
@@ -86,24 +86,25 @@ export class PyRepl extends BaseEvalElement {
|
||||
|
||||
const startState = EditorState.create({
|
||||
doc: this.code.trim(),
|
||||
extensions: extensions
|
||||
extensions: extensions,
|
||||
});
|
||||
|
||||
this.editor = new EditorView({
|
||||
state: startState,
|
||||
parent: this.editorNode
|
||||
parent: this.editorNode,
|
||||
});
|
||||
|
||||
const mainDiv = document.createElement('div');
|
||||
addClasses(mainDiv, ["parentBox", "flex", "flex-col", "mt-2", "mx-8", "relative"])
|
||||
addClasses(mainDiv, ['parentBox', 'flex', 'flex-col', 'mt-2', 'mx-8', 'relative']);
|
||||
|
||||
// add Editor to main PyScript div
|
||||
mainDiv.appendChild(this.editorNode);
|
||||
|
||||
// Play Button
|
||||
this.btnRun = document.createElement('button');
|
||||
this.btnRun.innerHTML = '<svg id="" class="svelte-fa svelte-ps5qeg" style="height:20px;width:20px;vertical-align:-.125em;transform-origin:center;overflow:visible;color:green" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>';
|
||||
addClasses(this.btnRun, ["absolute", "right-1", "bottom-3", "opacity-0", "group-hover:opacity-100"]);
|
||||
this.btnRun.innerHTML =
|
||||
'<svg id="" class="svelte-fa svelte-ps5qeg" style="height:20px;width:20px;vertical-align:-.125em;transform-origin:center;overflow:visible;color:green" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>';
|
||||
addClasses(this.btnRun, ['absolute', 'right-1', 'bottom-3', 'opacity-0', 'group-hover:opacity-100']);
|
||||
this.editorNode.appendChild(this.btnRun);
|
||||
|
||||
this.btnRun.onclick = wrap(this);
|
||||
@@ -117,7 +118,7 @@ export class PyRepl extends BaseEvalElement {
|
||||
|
||||
if (!this.id) {
|
||||
console.log(
|
||||
'WARNING: <pyrepl> define with an id. <pyrepl> should always have an id. More than one <pyrepl> on a page won\'t work otherwise!',
|
||||
"WARNING: <pyrepl> define with an id. <pyrepl> should always have an id. More than one <pyrepl> on a page won't work otherwise!",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,11 @@ mode.subscribe(value => {
|
||||
currentMode = value;
|
||||
});
|
||||
|
||||
|
||||
// TODO: use type declaractions
|
||||
type PyodideInterface = {
|
||||
registerJsModule(name: string, module: object): void;
|
||||
};
|
||||
|
||||
|
||||
export class PyScript extends BaseEvalElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -3,416 +3,6 @@ import { getLastPath } from './utils';
|
||||
let pyodideReadyPromise;
|
||||
let pyodide;
|
||||
|
||||
const additional_definitions = `
|
||||
from js import document, setInterval, console, setTimeout
|
||||
import micropip
|
||||
import time
|
||||
import asyncio
|
||||
import io, base64, sys
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
MIME_METHODS = {
|
||||
'__repr__': 'text/plain',
|
||||
'_repr_html_': 'text/html',
|
||||
'_repr_markdown_': 'text/markdown',
|
||||
'_repr_svg_': 'image/svg+xml',
|
||||
'_repr_png_': 'image/png',
|
||||
'_repr_pdf_': 'application/pdf',
|
||||
'_repr_jpeg_': 'image/jpeg',
|
||||
'_repr_latex': 'text/latex',
|
||||
'_repr_json_': 'application/json',
|
||||
'_repr_javascript_': 'application/javascript',
|
||||
'savefig': 'image/png'
|
||||
}
|
||||
|
||||
def render_image(mime, value, meta):
|
||||
data = f'data:{mime};charset=utf-8;base64,{value}'
|
||||
attrs = ' '.join(['{k}="{v}"' for k, v in meta.items()])
|
||||
return f'<img src="{data}" {attrs}</img>'
|
||||
|
||||
def identity(value, meta):
|
||||
return value
|
||||
|
||||
|
||||
MIME_RENDERERS = {
|
||||
'text/plain': identity,
|
||||
'text/html' : identity,
|
||||
'image/png' : lambda value, meta: render_image('image/png', value, meta),
|
||||
'image/jpeg': lambda value, meta: render_image('image/jpeg', value, meta),
|
||||
'image/svg+xml': identity,
|
||||
'application/json': identity,
|
||||
'application/javascript': lambda value, meta: f'<script>{value}</script>'
|
||||
}
|
||||
|
||||
|
||||
def eval_formatter(obj, print_method):
|
||||
"""
|
||||
Evaluates a formatter method.
|
||||
"""
|
||||
if print_method == '__repr__':
|
||||
return repr(obj)
|
||||
elif hasattr(obj, print_method):
|
||||
if print_method == 'savefig':
|
||||
buf = io.BytesIO()
|
||||
obj.savefig(buf, format='png')
|
||||
buf.seek(0)
|
||||
return base64.b64encode(buf.read()).decode('utf-8')
|
||||
return getattr(obj, print_method)()
|
||||
elif print_method == '_repr_mimebundle_':
|
||||
return {}, {}
|
||||
return None
|
||||
|
||||
|
||||
def format_mime(obj):
|
||||
"""
|
||||
Formats object using _repr_x_ methods.
|
||||
"""
|
||||
if isinstance(obj, str):
|
||||
return obj, 'text/plain'
|
||||
|
||||
mimebundle = eval_formatter(obj, '_repr_mimebundle_')
|
||||
if isinstance(mimebundle, tuple):
|
||||
format_dict, md_dict = mimebundle
|
||||
else:
|
||||
format_dict = mimebundle
|
||||
md_dict = {}
|
||||
|
||||
output, not_available = None, []
|
||||
for method, mime_type in reversed(MIME_METHODS.items()):
|
||||
if mime_type in format_dict:
|
||||
output = format_dict[mime_type]
|
||||
else:
|
||||
output = eval_formatter(obj, method)
|
||||
|
||||
if output is None:
|
||||
continue
|
||||
elif mime_type not in MIME_RENDERERS:
|
||||
not_available.append(mime_type)
|
||||
continue
|
||||
break
|
||||
if output is None:
|
||||
if not_available:
|
||||
console.warning(f'Rendered object requested unavailable MIME renderers: {not_available}')
|
||||
output = repr(output)
|
||||
mime_type = 'text/plain'
|
||||
elif isinstance(output, tuple):
|
||||
output, meta = output
|
||||
else:
|
||||
meta = {}
|
||||
return MIME_RENDERERS[mime_type](output, meta), mime_type
|
||||
|
||||
|
||||
class PyScript:
|
||||
loop = loop
|
||||
|
||||
@staticmethod
|
||||
def write(element_id, value, append=False, exec_id=0):
|
||||
"""Writes value to the element with id "element_id"""
|
||||
console.log(f"APPENDING: {append} ==> {element_id} --> {value}")
|
||||
if append:
|
||||
child = document.createElement('div');
|
||||
element = document.querySelector(f'#{element_id}');
|
||||
if not element:
|
||||
return
|
||||
exec_id = exec_id or element.childElementCount + 1
|
||||
element_id = child.id = f"{element_id}-{exec_id}";
|
||||
element.appendChild(child);
|
||||
|
||||
element = document.getElementById(element_id)
|
||||
html, mime_type = format_mime(value)
|
||||
if mime_type in ('application/javascript', 'text/html'):
|
||||
scriptEl = document.createRange().createContextualFragment(html)
|
||||
element.appendChild(scriptEl)
|
||||
else:
|
||||
element.innerHTML = html
|
||||
|
||||
@staticmethod
|
||||
def run_until_complete(f):
|
||||
p = loop.run_until_complete(f)
|
||||
|
||||
|
||||
class Element:
|
||||
def __init__(self, element_id, element=None):
|
||||
self._id = element_id
|
||||
self._element = element
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def element(self):
|
||||
"""Return the dom element"""
|
||||
if not self._element:
|
||||
self._element = document.querySelector(f'#{self._id}');
|
||||
return self._element
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.element.value
|
||||
|
||||
@property
|
||||
def innerHtml(self):
|
||||
return self.element.innerHtml
|
||||
|
||||
def write(self, value, append=False):
|
||||
console.log(f"Element.write: {value} --> {append}")
|
||||
# TODO: it should be the opposite... pyscript.write should use the Element.write
|
||||
# so we can consolidate on how we write depending on the element type
|
||||
pyscript.write(self._id, value, append=append)
|
||||
|
||||
def clear(self):
|
||||
if hasattr(self.element, 'value'):
|
||||
self.element.value = ''
|
||||
else:
|
||||
self.write("", append=False)
|
||||
|
||||
def select(self, query, from_content=False):
|
||||
el = self.element
|
||||
if from_content:
|
||||
el = el.content
|
||||
|
||||
_el = el.querySelector(query)
|
||||
if _el:
|
||||
return Element(_el.id, _el)
|
||||
else:
|
||||
console.log(f"WARNING: can't find element matching query {query}")
|
||||
|
||||
def clone(self, new_id=None, to=None):
|
||||
if new_id is None:
|
||||
new_id = self.element.id
|
||||
|
||||
clone = self.element.cloneNode(True);
|
||||
clone.id = new_id;
|
||||
|
||||
if to:
|
||||
to.element.appendChild(clone)
|
||||
|
||||
# Inject it into the DOM
|
||||
self.element.after(clone);
|
||||
|
||||
return Element(clone.id, clone)
|
||||
|
||||
def remove_class(self, classname):
|
||||
if isinstance(classname, list):
|
||||
for cl in classname:
|
||||
self.remove_class(cl)
|
||||
else:
|
||||
self.element.classList.remove(classname)
|
||||
|
||||
def add_class(self, classname):
|
||||
self.element.classList.add(classname)
|
||||
|
||||
def add_classes(element, class_list):
|
||||
for klass in class_list.split(' '):
|
||||
element.classList.add(klass)
|
||||
|
||||
def create(what, id_=None, classes=''):
|
||||
element = document.createElement(what)
|
||||
if id_:
|
||||
element.id = id_
|
||||
add_classes(element, classes)
|
||||
return Element(id_, element)
|
||||
|
||||
|
||||
class PyWidgetTheme:
|
||||
def __init__(self, main_style_classes):
|
||||
self.main_style_classes = main_style_classes
|
||||
|
||||
def theme_it(self, widget):
|
||||
for klass in self.main_style_classes.split(' '):
|
||||
widget.classList.add(klass)
|
||||
|
||||
|
||||
class PyItemTemplate(Element):
|
||||
label_fields = None
|
||||
|
||||
def __init__(self, data, labels=None, state_key=None, parent=None):
|
||||
self.data = data
|
||||
|
||||
self.register_parent(parent)
|
||||
|
||||
if not labels:
|
||||
labels = list(self.data.keys())
|
||||
self.labels = labels
|
||||
|
||||
self.state_key = state_key
|
||||
|
||||
super().__init__(self._id)
|
||||
|
||||
def register_parent(self, parent):
|
||||
self._parent = parent
|
||||
if parent:
|
||||
self._id = f"{self._parent._id}-c-{len(self._parent._children)}"
|
||||
self.data['id'] = self._id
|
||||
else:
|
||||
self._id = None
|
||||
|
||||
def create(self):
|
||||
console.log('creating section')
|
||||
new_child = create('section', self._id, "task bg-white my-1")
|
||||
console.log('creating values')
|
||||
|
||||
console.log('creating innerHtml')
|
||||
new_child._element.innerHTML = f"""
|
||||
<label for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox" class="task-check">
|
||||
<p class="m-0 inline">{self.render_content()}</p>
|
||||
</label>
|
||||
"""
|
||||
|
||||
console.log('returning')
|
||||
return new_child
|
||||
|
||||
def on_click(self, evt):
|
||||
pass
|
||||
|
||||
def pre_append(self):
|
||||
pass
|
||||
|
||||
def post_append(self):
|
||||
self.element.click = self.on_click
|
||||
self.element.onclick = self.on_click
|
||||
|
||||
self._post_append()
|
||||
|
||||
def _post_append(self):
|
||||
pass
|
||||
|
||||
def strike(self, value, extra=None):
|
||||
if value:
|
||||
self.add_class("line-through")
|
||||
else:
|
||||
self.remove_class("line-through")
|
||||
|
||||
def render_content(self):
|
||||
return ' - '.join([self.data[f] for f in self.labels])
|
||||
|
||||
class PyListTemplate:
|
||||
theme = PyWidgetTheme("flex flex-col-reverse mt-8 mx-8")
|
||||
item_class = PyItemTemplate
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self._children = []
|
||||
self._id = self.parent.id
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
return self._children
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return [c.data for c in self._children]
|
||||
|
||||
def render_children(self):
|
||||
out = []
|
||||
binds = {}
|
||||
for i, c in enumerate(self._children):
|
||||
txt = c.element.innerHTML
|
||||
rnd = str(time.time()).replace(".", "")[-5:]
|
||||
new_id = f"{c.element.id}-{i}-{rnd}"
|
||||
binds[new_id] = c.element.id
|
||||
txt = txt.replace(">", f" id='{new_id}'>")
|
||||
print(txt)
|
||||
|
||||
def foo(evt):
|
||||
console.log(evt)
|
||||
evtEl = evt.srcElement
|
||||
srcEl = Element(binds[evtEl.id])
|
||||
srcEl.element.onclick()
|
||||
evtEl.classList = srcEl.element.classList
|
||||
|
||||
for new_id, old_id in binds.items():
|
||||
Element(new_id).element.onclick = foo
|
||||
|
||||
def connect(self):
|
||||
self.md = main_div = document.createElement('div')
|
||||
main_div.id = self._id + "-list-tasks-container"
|
||||
|
||||
if self.theme:
|
||||
self.theme.theme_it(main_div)
|
||||
|
||||
self.parent.appendChild(main_div)
|
||||
|
||||
def add(self, *args, **kws):
|
||||
if not isinstance(args[0], self.item_class):
|
||||
child = self.item_class(*args, **kws)
|
||||
else:
|
||||
child = args[0]
|
||||
child.register_parent(self)
|
||||
return self._add(child)
|
||||
|
||||
def _add(self, child_elem):
|
||||
console.log("appending child", child_elem.element)
|
||||
self.pre_child_append(child_elem)
|
||||
child_elem.pre_append()
|
||||
self._children.append(child_elem)
|
||||
self.md.appendChild(child_elem.create().element)
|
||||
child_elem.post_append()
|
||||
self.child_appended(child_elem)
|
||||
return child_elem
|
||||
|
||||
def pre_child_append(self, child):
|
||||
pass
|
||||
|
||||
def child_appended(self, child):
|
||||
"""Overwrite me to define logic"""
|
||||
pass
|
||||
|
||||
|
||||
class OutputCtxManager:
|
||||
def __init__(self, out=None, output_to_console=True, append=True):
|
||||
self._out = out
|
||||
self._prev = out
|
||||
self.output_to_console = output_to_console
|
||||
self._append = append
|
||||
|
||||
def change(self, out=None, err=None, output_to_console=True, append=True):
|
||||
self._prev = self._out
|
||||
self._out = out
|
||||
self.output_to_console = output_to_console
|
||||
self._append = append
|
||||
console.log("----> changed out to", self._out, self._append)
|
||||
|
||||
def revert(self):
|
||||
console.log("----> reverted")
|
||||
self._out = self._prev
|
||||
|
||||
def write(self, txt):
|
||||
console.log('writing to', self._out, txt, self._append)
|
||||
if self._out:
|
||||
pyscript.write(self._out, txt, append=self._append)
|
||||
if self.output_to_console:
|
||||
console.log(self._out, txt)
|
||||
|
||||
class OutputManager:
|
||||
def __init__(self, out=None, err=None, output_to_console=True, append=True):
|
||||
sys.stdout = self._out_manager = OutputCtxManager(out, output_to_console, append)
|
||||
sys.stderr = self._err_manager = OutputCtxManager(err, output_to_console, append)
|
||||
self.output_to_console = output_to_console
|
||||
self._append = append
|
||||
|
||||
def change(self, out=None, err=None, output_to_console=True, append=True):
|
||||
self._out_manager.change(out, output_to_console, append)
|
||||
sys.stdout = self._out_manager
|
||||
self._err_manager.change(err, output_to_console, append)
|
||||
sys.stderr = self._err_manager
|
||||
self.output_to_console = output_to_console
|
||||
self.append = append
|
||||
|
||||
def revert(self):
|
||||
self._out_manager.revert()
|
||||
self._err_manager.revert()
|
||||
sys.stdout = self._out_manager
|
||||
sys.stderr = self._err_manager
|
||||
console.log("----> reverted")
|
||||
|
||||
pyscript = PyScript()
|
||||
output_manager = OutputManager()
|
||||
`;
|
||||
|
||||
const loadInterpreter = async function (): Promise<any> {
|
||||
console.log('creating pyodide runtime');
|
||||
// eslint-disable-next-line
|
||||
@@ -426,10 +16,17 @@ const loadInterpreter = async function (): Promise<any> {
|
||||
// now that we loaded, add additional convenience functions
|
||||
console.log('loading micropip');
|
||||
await pyodide.loadPackage('micropip');
|
||||
console.log('loading pyscript module');
|
||||
|
||||
console.log('creating additional definitions');
|
||||
const output = pyodide.runPython(additional_definitions);
|
||||
console.log('loading pyscript...');
|
||||
|
||||
// let's get the full path of where PyScript is running from so we can load the pyscript.py
|
||||
// file from the same location
|
||||
const loadedScript: HTMLScriptElement = document.querySelector(`script[src$='pyscript.js']`);
|
||||
const scriptPath = loadedScript.src.substr(0, loadedScript.src.lastIndexOf('/'));
|
||||
await pyodide.runPythonAsync(await (await fetch(`${scriptPath}/pyscript.py`)).text());
|
||||
|
||||
console.log(scriptPath);
|
||||
|
||||
console.log('done setting up environment');
|
||||
return pyodide;
|
||||
};
|
||||
@@ -445,7 +42,7 @@ const loadFromFile = async function (s: string, runtime: any): Promise<any> {
|
||||
await runtime.runPythonAsync(
|
||||
`
|
||||
from pyodide.http import pyfetch
|
||||
|
||||
from js import console
|
||||
response = await pyfetch("` +
|
||||
s +
|
||||
`")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import io
|
||||
import sys
|
||||
import time
|
||||
|
||||
from js import console, document
|
||||
import micropip
|
||||
from js import console, document, setInterval, setTimeout
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
@@ -135,6 +138,10 @@ class Element:
|
||||
self._id = element_id
|
||||
self._element = element
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def element(self):
|
||||
"""Return the dom element"""
|
||||
@@ -142,6 +149,14 @@ class Element:
|
||||
self._element = document.querySelector(f"#{self._id}")
|
||||
return self._element
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.element.value
|
||||
|
||||
@property
|
||||
def innerHtml(self):
|
||||
return self.element.innerHtml
|
||||
|
||||
def write(self, value, append=False):
|
||||
console.log(f"Element.write: {value} --> {append}")
|
||||
# TODO: it should be the opposite... pyscript.write should use the Element.write
|
||||
@@ -179,3 +194,230 @@ class Element:
|
||||
self.element.after(clone)
|
||||
|
||||
return Element(clone.id, clone)
|
||||
|
||||
def remove_class(self, classname):
|
||||
if isinstance(classname, list):
|
||||
for cl in classname:
|
||||
self.remove_class(cl)
|
||||
else:
|
||||
self.element.classList.remove(classname)
|
||||
|
||||
def add_class(self, classname):
|
||||
self.element.classList.add(classname)
|
||||
|
||||
|
||||
def add_classes(element, class_list):
|
||||
for klass in class_list.split(" "):
|
||||
element.classList.add(klass)
|
||||
|
||||
|
||||
def create(what, id_=None, classes=""):
|
||||
element = document.createElement(what)
|
||||
if id_:
|
||||
element.id = id_
|
||||
add_classes(element, classes)
|
||||
return Element(id_, element)
|
||||
|
||||
|
||||
class PyWidgetTheme:
|
||||
def __init__(self, main_style_classes):
|
||||
self.main_style_classes = main_style_classes
|
||||
|
||||
def theme_it(self, widget):
|
||||
for klass in self.main_style_classes.split(" "):
|
||||
widget.classList.add(klass)
|
||||
|
||||
|
||||
class PyItemTemplate(Element):
|
||||
label_fields = None
|
||||
|
||||
def __init__(self, data, labels=None, state_key=None, parent=None):
|
||||
self.data = data
|
||||
|
||||
self.register_parent(parent)
|
||||
|
||||
if not labels:
|
||||
labels = list(self.data.keys())
|
||||
self.labels = labels
|
||||
|
||||
self.state_key = state_key
|
||||
|
||||
super().__init__(self._id)
|
||||
|
||||
def register_parent(self, parent):
|
||||
self._parent = parent
|
||||
if parent:
|
||||
self._id = f"{self._parent._id}-c-{len(self._parent._children)}"
|
||||
self.data["id"] = self._id
|
||||
else:
|
||||
self._id = None
|
||||
|
||||
def create(self):
|
||||
console.log("creating section")
|
||||
new_child = create("section", self._id, "task bg-white my-1")
|
||||
console.log("creating values")
|
||||
|
||||
console.log("creating innerHtml")
|
||||
new_child._element.innerHTML = f"""
|
||||
<label for="flex items-center p-2 ">
|
||||
<input class="mr-2" type="checkbox" class="task-check">
|
||||
<p class="m-0 inline">{self.render_content()}</p>
|
||||
</label>
|
||||
"""
|
||||
|
||||
console.log("returning")
|
||||
return new_child
|
||||
|
||||
def on_click(self, evt):
|
||||
pass
|
||||
|
||||
def pre_append(self):
|
||||
pass
|
||||
|
||||
def post_append(self):
|
||||
self.element.click = self.on_click
|
||||
self.element.onclick = self.on_click
|
||||
|
||||
self._post_append()
|
||||
|
||||
def _post_append(self):
|
||||
pass
|
||||
|
||||
def strike(self, value, extra=None):
|
||||
if value:
|
||||
self.add_class("line-through")
|
||||
else:
|
||||
self.remove_class("line-through")
|
||||
|
||||
def render_content(self):
|
||||
return " - ".join([self.data[f] for f in self.labels])
|
||||
|
||||
|
||||
class PyListTemplate:
|
||||
theme = PyWidgetTheme("flex flex-col-reverse mt-8 mx-8")
|
||||
item_class = PyItemTemplate
|
||||
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self._children = []
|
||||
self._id = self.parent.id
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
return self._children
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return [c.data for c in self._children]
|
||||
|
||||
def render_children(self):
|
||||
out = []
|
||||
binds = {}
|
||||
for i, c in enumerate(self._children):
|
||||
txt = c.element.innerHTML
|
||||
rnd = str(time.time()).replace(".", "")[-5:]
|
||||
new_id = f"{c.element.id}-{i}-{rnd}"
|
||||
binds[new_id] = c.element.id
|
||||
txt = txt.replace(">", f" id='{new_id}'>")
|
||||
print(txt)
|
||||
|
||||
def foo(evt):
|
||||
console.log(evt)
|
||||
evtEl = evt.srcElement
|
||||
srcEl = Element(binds[evtEl.id])
|
||||
srcEl.element.onclick()
|
||||
evtEl.classList = srcEl.element.classList
|
||||
|
||||
for new_id, old_id in binds.items():
|
||||
Element(new_id).element.onclick = foo
|
||||
|
||||
def connect(self):
|
||||
self.md = main_div = document.createElement("div")
|
||||
main_div.id = self._id + "-list-tasks-container"
|
||||
|
||||
if self.theme:
|
||||
self.theme.theme_it(main_div)
|
||||
|
||||
self.parent.appendChild(main_div)
|
||||
|
||||
def add(self, *args, **kws):
|
||||
if not isinstance(args[0], self.item_class):
|
||||
child = self.item_class(*args, **kws)
|
||||
else:
|
||||
child = args[0]
|
||||
child.register_parent(self)
|
||||
return self._add(child)
|
||||
|
||||
def _add(self, child_elem):
|
||||
console.log("appending child", child_elem.element)
|
||||
self.pre_child_append(child_elem)
|
||||
child_elem.pre_append()
|
||||
self._children.append(child_elem)
|
||||
self.md.appendChild(child_elem.create().element)
|
||||
child_elem.post_append()
|
||||
self.child_appended(child_elem)
|
||||
return child_elem
|
||||
|
||||
def pre_child_append(self, child):
|
||||
pass
|
||||
|
||||
def child_appended(self, child):
|
||||
"""Overwrite me to define logic"""
|
||||
pass
|
||||
|
||||
|
||||
class OutputCtxManager:
|
||||
def __init__(self, out=None, output_to_console=True, append=True):
|
||||
self._out = out
|
||||
self._prev = out
|
||||
self.output_to_console = output_to_console
|
||||
self._append = append
|
||||
|
||||
def change(self, out=None, err=None, output_to_console=True, append=True):
|
||||
self._prev = self._out
|
||||
self._out = out
|
||||
self.output_to_console = output_to_console
|
||||
self._append = append
|
||||
console.log("----> changed out to", self._out, self._append)
|
||||
|
||||
def revert(self):
|
||||
console.log("----> reverted")
|
||||
self._out = self._prev
|
||||
|
||||
def write(self, txt):
|
||||
console.log("writing to", self._out, txt, self._append)
|
||||
if self._out:
|
||||
pyscript.write(self._out, txt, append=self._append)
|
||||
if self.output_to_console:
|
||||
console.log(self._out, txt)
|
||||
|
||||
|
||||
class OutputManager:
|
||||
def __init__(self, out=None, err=None, output_to_console=True, append=True):
|
||||
sys.stdout = self._out_manager = OutputCtxManager(
|
||||
out, output_to_console, append
|
||||
)
|
||||
sys.stderr = self._err_manager = OutputCtxManager(
|
||||
err, output_to_console, append
|
||||
)
|
||||
self.output_to_console = output_to_console
|
||||
self._append = append
|
||||
|
||||
def change(self, out=None, err=None, output_to_console=True, append=True):
|
||||
self._out_manager.change(out, output_to_console, append)
|
||||
sys.stdout = self._out_manager
|
||||
self._err_manager.change(err, output_to_console, append)
|
||||
sys.stderr = self._err_manager
|
||||
self.output_to_console = output_to_console
|
||||
self.append = append
|
||||
|
||||
def revert(self):
|
||||
self._out_manager.revert()
|
||||
self._err_manager.revert()
|
||||
sys.stdout = self._out_manager
|
||||
sys.stderr = self._err_manager
|
||||
console.log("----> reverted")
|
||||
|
||||
|
||||
pyscript = PyScript()
|
||||
output_manager = OutputManager()
|
||||
|
||||
Reference in New Issue
Block a user