mirror of
https://github.com/pyscript/pyscript.git
synced 2022-05-01 19:47:48 +03:00
Merge branch 'main' into out-of-box
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -127,3 +127,5 @@ dmypy.json
|
|||||||
|
|
||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.pyre/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
|||||||
38
README.md
38
README.md
@@ -1,3 +1,39 @@
|
|||||||
# pyscript
|
# PyScript
|
||||||
|
|
||||||
|
## What is PyScript
|
||||||
|
|
||||||
|
### tl;dr
|
||||||
|
PyScript is a Pythonic alternative to Scratch, JSFiddle or other "easy to use" programming frameworks, making the web a friendly, hackable, place where anyone can author interesting and interactive applications.
|
||||||
|
|
||||||
To demonstrate pyscript, see [the pyscript folder](pyscriptjs/README.md).
|
To demonstrate pyscript, see [the pyscript folder](pyscriptjs/README.md).
|
||||||
|
|
||||||
|
### Longer Version
|
||||||
|
PyScript is a meta project that aims to combine multiple open technologies to create a framework for users to use Python (and other languages) to create sophisticated applications in the browser. It highly integrate with the way the DOM works in the browser and allows users to add logic, in Python, in a way that feel natural to web as well as Python developers.
|
||||||
|
|
||||||
|
## Try PyScript
|
||||||
|
|
||||||
|
To try PyScript, import the pyscript to your html page with:
|
||||||
|
```
|
||||||
|
<link rel="stylesheet" href="pyscript.css" />
|
||||||
|
<script defer src="pyscript.js"></script>
|
||||||
|
```
|
||||||
|
At that point, you can then use PyScript components in your html page. PyScript currently implements the following elements:
|
||||||
|
|
||||||
|
* `<py-script>`: that can be used to define python code that is execute withing the web page. The element itself is not rendered to the page and only used to add logic
|
||||||
|
* `<py-repl>`: creates a REPL component that is rendered to the page as a code editor and allows users to right code that can be executed
|
||||||
|
|
||||||
|
Check out the `/examples` folder for more examples on how to use it, all you need to do is open them in Chrome.
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
To contribute:
|
||||||
|
|
||||||
|
* clone the repo
|
||||||
|
* cd into the main project folder with `cd pyscriptjs`
|
||||||
|
* install the dependencies with `npm install`
|
||||||
|
* run `npm run dev` to build and run the dev server. This will also watch for changes and rebuild when a file is saved
|
||||||
|
|
||||||
|
## Notes:
|
||||||
|
|
||||||
|
* This is an extremely experimental project, so expect things to break!
|
||||||
|
* PyScript has been only tested on Chrome, at the moment.
|
||||||
|
|||||||
4883
pyscriptjs/package-lock.json
generated
Normal file
4883
pyscriptjs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "NODE_ENV=production rollup -c",
|
"build": "NODE_ENV=production rollup -c",
|
||||||
"dev": "rollup -c -w",
|
"dev": "rollup -c -w",
|
||||||
"start": "sirv public",
|
"start": "sirv public --no-clear --port 8080",
|
||||||
"validate": "svelte-check"
|
"validate": "svelte-check"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<link rel="stylesheet" href="build/bundle.css" />
|
<link rel="stylesheet" href="build/bundle.css" />
|
||||||
|
|
||||||
<script defer src="build/bundle.js"></script>
|
<script defer src="build/pyscript.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<link rel="stylesheet" href="build/bundle.css" />
|
<link rel="stylesheet" href="build/bundle.css" />
|
||||||
|
|
||||||
<script defer src="build/bundle.js"></script>
|
<script defer src="build/pyscript.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<link rel="stylesheet" href="build/bundle.css" />
|
<link rel="stylesheet" href="build/bundle.css" />
|
||||||
|
|
||||||
<script defer src="build/bundle.js"></script>
|
<script defer src="build/pyscript.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<link rel="stylesheet" href="build/bundle.css" />
|
<link rel="stylesheet" href="build/bundle.css" />
|
||||||
|
|
||||||
<script defer src="build/bundle.js"></script>
|
<script defer src="build/pyscript.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default {
|
|||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
format: "iife",
|
format: "iife",
|
||||||
name: "app",
|
name: "app",
|
||||||
file: "public/build/bundle.js",
|
file: "public/build/pyscript.js",
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
svelte({
|
svelte({
|
||||||
@@ -54,7 +54,7 @@ export default {
|
|||||||
dev: !production,
|
dev: !production,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
css({ output: "bundle.css" }),
|
css({ output: "pyscript.css" }),
|
||||||
resolve({
|
resolve({
|
||||||
browser: true,
|
browser: true,
|
||||||
dedupe: ["svelte"],
|
dedupe: ["svelte"],
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
|
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
|
||||||
import Tailwind from "./Tailwind.svelte";
|
import Tailwind from "./Tailwind.svelte";
|
||||||
import { loadInterpreter } from './interpreter';
|
import { loadInterpreter } from './interpreter';
|
||||||
import { pyodideLoaded, loadedEnvironments, navBarOpen, componentsNavOpen, mode, scriptsQueue, initializers } from './stores';
|
import { pyodideLoaded, loadedEnvironments, navBarOpen, componentsNavOpen, mode, scriptsQueue, initializers, postInitializers } from './stores';
|
||||||
import Main from "./Main.svelte";
|
import Main from "./Main.svelte";
|
||||||
import Header from "./Header.svelte";
|
import Header from "./Header.svelte";
|
||||||
import SideNav from "./SideNav.svelte";
|
import SideNav from "./SideNav.svelte";
|
||||||
@@ -42,16 +42,21 @@
|
|||||||
showNavBar = value;
|
showNavBar = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// now we call all initializers before we actually executed all page scripts
|
||||||
|
for (let initializer of $initializers){
|
||||||
|
initializer();
|
||||||
|
}
|
||||||
|
|
||||||
// now we can actually execute the page scripts if we are in play mode
|
// now we can actually execute the page scripts if we are in play mode
|
||||||
if ($mode == "play"){
|
if ($mode == "play"){
|
||||||
for (let script of $scriptsQueue) {
|
for (let script of $scriptsQueue) {
|
||||||
script.evaluate();
|
script.evaluate();
|
||||||
}
|
}
|
||||||
scriptsQueue.set([])
|
scriptsQueue.set([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we call all initializers AFTER we actually executed all page scripts
|
// now we call all post initializers AFTER we actually executed all page scripts
|
||||||
for (let initializer of $initializers){
|
for (let initializer of $postInitializers){
|
||||||
initializer();
|
initializer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { keymap, ViewUpdate } from "@codemirror/view";
|
|||||||
import { defaultKeymap } from "@codemirror/commands";
|
import { defaultKeymap } from "@codemirror/commands";
|
||||||
import { oneDarkTheme } from "@codemirror/theme-one-dark";
|
import { oneDarkTheme } from "@codemirror/theme-one-dark";
|
||||||
|
|
||||||
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue, addInitializer } from '../stores';
|
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue, addInitializer, addPostInitializer } from '../stores';
|
||||||
import { addClasses } from '../utils';
|
import { addClasses } from '../utils';
|
||||||
|
|
||||||
// Premise used to connect to the first available pyodide interpreter
|
// Premise used to connect to the first available pyodide interpreter
|
||||||
@@ -40,6 +40,10 @@ function createCmdHandler(el){
|
|||||||
return toggleCheckbox
|
return toggleCheckbox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function htmlDecode(input) {
|
||||||
|
var doc = new DOMParser().parseFromString(input, "text/html");
|
||||||
|
return doc.documentElement.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
class Script {
|
class Script {
|
||||||
source: string;
|
source: string;
|
||||||
@@ -214,7 +218,7 @@ export class PyScript extends HTMLElement {
|
|||||||
// debugger
|
// debugger
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let source = this.editor.state.doc.toString();
|
let source = htmlDecode(this.editor.state.doc.toString());
|
||||||
let output;
|
let output;
|
||||||
if (source.includes("asyncio")){
|
if (source.includes("asyncio")){
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
@@ -232,6 +236,7 @@ export class PyScript extends HTMLElement {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.addToOutput(err);
|
this.addToOutput(err);
|
||||||
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,9 +246,8 @@ export class PyScript extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Initialize all elements with py-onClick handlers attributes */
|
||||||
async function initHandlers() {
|
async function initHandlers() {
|
||||||
if( handlersCollected == true ) return;
|
|
||||||
|
|
||||||
console.log('Collecting nodes...');
|
console.log('Collecting nodes...');
|
||||||
let pyodide = await pyodideReadyPromise;
|
let pyodide = await pyodideReadyPromise;
|
||||||
let matches : NodeListOf<HTMLElement> = document.querySelectorAll('[pys-onClick]');
|
let matches : NodeListOf<HTMLElement> = document.querySelectorAll('[pys-onClick]');
|
||||||
@@ -254,6 +258,7 @@ async function initHandlers() {
|
|||||||
source = `Element("${ el.id }").element.onclick = ${ handlerCode }`;
|
source = `Element("${ el.id }").element.onclick = ${ handlerCode }`;
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
|
|
||||||
|
// TODO: Should we actually map handlers in JS instaed of Python?
|
||||||
// el.onclick = (evt: any) => {
|
// el.onclick = (evt: any) => {
|
||||||
// console.log("click");
|
// console.log("click");
|
||||||
// new Promise((resolve, reject) => {
|
// new Promise((resolve, reject) => {
|
||||||
@@ -276,11 +281,22 @@ async function initHandlers() {
|
|||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addInitializer(initHandlers)
|
|
||||||
|
|
||||||
// if( document.readyState === 'loading' ) {
|
/** Mount all elements with attribute py-mount into the Python namespace */
|
||||||
// document.addEventListener( 'DOMContentLoaded', initHandlers );
|
async function mountElements() {
|
||||||
// }
|
console.log('Collecting nodes to be mounted into python namespace...');
|
||||||
// else if( document.readyState === 'interactive' || document.readyState === 'complete' ) {
|
let pyodide = await pyodideReadyPromise;
|
||||||
// initHandlers();
|
let matches : NodeListOf<HTMLElement> = document.querySelectorAll('[py-mount]');
|
||||||
// }
|
let output;
|
||||||
|
let source = "";
|
||||||
|
for (var el of matches) {
|
||||||
|
let mountName = el.getAttribute('py-mount');
|
||||||
|
if (!mountName){
|
||||||
|
mountName = el.id.replace("-", "_");
|
||||||
|
}
|
||||||
|
source += `\n${ mountName } = Element("${ el.id }")`;
|
||||||
|
}
|
||||||
|
await pyodide.runPythonAsync(source);
|
||||||
|
}
|
||||||
|
addPostInitializer(initHandlers);
|
||||||
|
addInitializer(mountElements);
|
||||||
|
|||||||
@@ -102,11 +102,12 @@ let loadInterpreter = async function(): any {
|
|||||||
/* @ts-ignore */
|
/* @ts-ignore */
|
||||||
let pyodide = await loadPyodide({
|
let pyodide = await loadPyodide({
|
||||||
indexURL: "https://cdn.jsdelivr.net/pyodide/v0.19.0/full/",
|
indexURL: "https://cdn.jsdelivr.net/pyodide/v0.19.0/full/",
|
||||||
stdout: console.log
|
stdout: console.log,
|
||||||
|
stderr: console.log
|
||||||
});
|
});
|
||||||
|
|
||||||
// now that we loaded, add additional convenience fuctions
|
// now that we loaded, add additional convenience fuctions
|
||||||
// pyodide.loadPackage(['matplotlib', 'numpy'])
|
pyodide.loadPackage(['matplotlib', 'numpy', 'bokeh'])
|
||||||
|
|
||||||
await pyodide.loadPackage("micropip");
|
await pyodide.loadPackage("micropip");
|
||||||
// await pyodide.runPythonAsync(`
|
// await pyodide.runPythonAsync(`
|
||||||
|
|||||||
@@ -24,9 +24,11 @@ export const currentComponentDetails = writable([]);
|
|||||||
export const mode = writable(DEFAULT_MODE)
|
export const mode = writable(DEFAULT_MODE)
|
||||||
export const scriptsQueue = writable([])
|
export const scriptsQueue = writable([])
|
||||||
export const initializers = writable([])
|
export const initializers = writable([])
|
||||||
|
export const postInitializers = writable([])
|
||||||
|
|
||||||
let scriptsQueue_ = [];
|
let scriptsQueue_ = [];
|
||||||
let initializers_ = [];
|
let initializers_ = [];
|
||||||
|
let postInitializers_ = [];
|
||||||
|
|
||||||
scriptsQueue.subscribe(value => {
|
scriptsQueue.subscribe(value => {
|
||||||
scriptsQueue_ = value;
|
scriptsQueue_ = value;
|
||||||
@@ -40,6 +42,22 @@ scriptsQueue.subscribe(value => {
|
|||||||
scriptsQueue_ = value;
|
scriptsQueue_ = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initializers.subscribe(value => {
|
||||||
|
initializers_ = value;
|
||||||
|
});
|
||||||
|
|
||||||
export const addInitializer = (initializer) => {
|
export const addInitializer = (initializer) => {
|
||||||
|
console.log("adding initializer", initializer);
|
||||||
initializers.set([...initializers_, initializer]);
|
initializers.set([...initializers_, initializer]);
|
||||||
|
console.log("adding initializer", initializer);
|
||||||
|
};
|
||||||
|
|
||||||
|
postInitializers.subscribe(value => {
|
||||||
|
postInitializers_ = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addPostInitializer = (initializer) => {
|
||||||
|
console.log("adding post initializer", initializer);
|
||||||
|
postInitializers.set([...postInitializers_, initializer]);
|
||||||
|
console.log("adding post initializer", initializer);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user