1
0
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:
Fabio Pliger
2022-04-01 10:53:30 -05:00
committed by GitHub
13 changed files with 4986 additions and 25 deletions

2
.gitignore vendored
View File

@@ -127,3 +127,5 @@ dmypy.json
# Pyre type checker
.pyre/
node_modules/

View File

@@ -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).
### 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

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
"scripts": {
"build": "NODE_ENV=production rollup -c",
"dev": "rollup -c -w",
"start": "sirv public",
"start": "sirv public --no-clear --port 8080",
"validate": "svelte-check"
},
"devDependencies": {

View File

@@ -9,7 +9,7 @@
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="build/bundle.css" />
<script defer src="build/bundle.js"></script>
<script defer src="build/pyscript.js"></script>
</head>
<body>

View File

@@ -9,7 +9,7 @@
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="build/bundle.css" />
<script defer src="build/bundle.js"></script>
<script defer src="build/pyscript.js"></script>
</head>
<body>

View File

@@ -9,7 +9,7 @@
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="build/bundle.css" />
<script defer src="build/bundle.js"></script>
<script defer src="build/pyscript.js"></script>
</head>
<body>

View File

@@ -9,7 +9,7 @@
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="build/bundle.css" />
<script defer src="build/bundle.js"></script>
<script defer src="build/pyscript.js"></script>
</head>
<body>

View File

@@ -40,7 +40,7 @@ export default {
sourcemap: true,
format: "iife",
name: "app",
file: "public/build/bundle.js",
file: "public/build/pyscript.js",
},
plugins: [
svelte({
@@ -54,7 +54,7 @@ export default {
dev: !production,
},
}),
css({ output: "bundle.css" }),
css({ output: "pyscript.css" }),
resolve({
browser: true,
dedupe: ["svelte"],

View File

@@ -4,7 +4,7 @@
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'
import Tailwind from "./Tailwind.svelte";
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 Header from "./Header.svelte";
import SideNav from "./SideNav.svelte";
@@ -42,16 +42,21 @@
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
if ($mode == "play"){
for (let script of $scriptsQueue) {
script.evaluate();
}
scriptsQueue.set([])
scriptsQueue.set([]);
}
// now we call all initializers AFTER we actually executed all page scripts
for (let initializer of $initializers){
// now we call all post initializers AFTER we actually executed all page scripts
for (let initializer of $postInitializers){
initializer();
}
}

View File

@@ -6,7 +6,7 @@ import { keymap, ViewUpdate } from "@codemirror/view";
import { defaultKeymap } from "@codemirror/commands";
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';
// Premise used to connect to the first available pyodide interpreter
@@ -40,6 +40,10 @@ function createCmdHandler(el){
return toggleCheckbox
}
function htmlDecode(input) {
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}
class Script {
source: string;
@@ -214,7 +218,7 @@ export class PyScript extends HTMLElement {
// debugger
try {
// @ts-ignore
let source = this.editor.state.doc.toString();
let source = htmlDecode(this.editor.state.doc.toString());
let output;
if (source.includes("asyncio")){
output = await pyodide.runPythonAsync(source);
@@ -232,6 +236,7 @@ export class PyScript extends HTMLElement {
}
} catch (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() {
if( handlersCollected == true ) return;
console.log('Collecting nodes...');
let pyodide = await pyodideReadyPromise;
let matches : NodeListOf<HTMLElement> = document.querySelectorAll('[pys-onClick]');
@@ -254,6 +258,7 @@ async function initHandlers() {
source = `Element("${ el.id }").element.onclick = ${ handlerCode }`;
output = await pyodide.runPythonAsync(source);
// TODO: Should we actually map handlers in JS instaed of Python?
// el.onclick = (evt: any) => {
// console.log("click");
// new Promise((resolve, reject) => {
@@ -276,11 +281,22 @@ async function initHandlers() {
output = await pyodide.runPythonAsync(source);
}
}
addInitializer(initHandlers)
// if( document.readyState === 'loading' ) {
// document.addEventListener( 'DOMContentLoaded', initHandlers );
// }
// else if( document.readyState === 'interactive' || document.readyState === 'complete' ) {
// initHandlers();
// }
/** Mount all elements with attribute py-mount into the Python namespace */
async function mountElements() {
console.log('Collecting nodes to be mounted into python namespace...');
let pyodide = await pyodideReadyPromise;
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);

View File

@@ -102,11 +102,12 @@ let loadInterpreter = async function(): any {
/* @ts-ignore */
let pyodide = await loadPyodide({
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
// pyodide.loadPackage(['matplotlib', 'numpy'])
pyodide.loadPackage(['matplotlib', 'numpy', 'bokeh'])
await pyodide.loadPackage("micropip");
// await pyodide.runPythonAsync(`

View File

@@ -24,9 +24,11 @@ export const currentComponentDetails = writable([]);
export const mode = writable(DEFAULT_MODE)
export const scriptsQueue = writable([])
export const initializers = writable([])
export const postInitializers = writable([])
let scriptsQueue_ = [];
let initializers_ = [];
let postInitializers_ = [];
scriptsQueue.subscribe(value => {
scriptsQueue_ = value;
@@ -40,6 +42,22 @@ scriptsQueue.subscribe(value => {
scriptsQueue_ = value;
});
initializers.subscribe(value => {
initializers_ = value;
});
export const addInitializer = (initializer) => {
console.log("adding initializer", 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);
};