mirror of
https://github.com/pyscript/pyscript.git
synced 2022-05-01 19:47:48 +03:00
Merge pull request #10 from anaconda/pys-5/load-from-source
[Pys-5] load from source
This commit is contained in:
@@ -7,50 +7,14 @@
|
|||||||
<title>Todo App</title>
|
<title>Todo App</title>
|
||||||
|
|
||||||
<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/pyscript.css" />
|
||||||
|
|
||||||
<script defer src="../build/pyscript.js"></script>
|
<script defer src="/build/pyscript.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<py-script>
|
<body class="container">
|
||||||
from datetime import datetime as dt
|
<!-- <py-repl id="my-repl" auto-generate="true"> </py-repl> -->
|
||||||
|
<py-script src="/todo.py"> </py-script>
|
||||||
tasks = []
|
|
||||||
|
|
||||||
# define the task template that will be use to render new templates to the page
|
|
||||||
task_template = Element("task-template").select('.task', from_content=True)
|
|
||||||
task_list = Element("list-tasks-container")
|
|
||||||
new_task_content = Element("new-task-content")
|
|
||||||
|
|
||||||
def add_task(*ags, **kws):
|
|
||||||
# create task
|
|
||||||
task_id = f"task-{len(tasks)}"
|
|
||||||
task = {"id": task_id, "content": new_task_content.element.value, "done": False, "created_at": dt.now()}
|
|
||||||
tasks.append(task)
|
|
||||||
|
|
||||||
# add the task element to the page as new node in the list by cloning from a template
|
|
||||||
taskHtml = task_template.clone(task_id, to=task_list)
|
|
||||||
taskHtmlContent = taskHtml.select('p')
|
|
||||||
taskHtmlContent.element.innerText = task['content']
|
|
||||||
taskHtmlCheck = taskHtml.select('input')
|
|
||||||
task_list.element.appendChild(taskHtml.element)
|
|
||||||
|
|
||||||
def check_task(evt=None):
|
|
||||||
task['done'] = not task['done']
|
|
||||||
if task['done']:
|
|
||||||
taskHtmlContent.element.classList.add("line-through")
|
|
||||||
else:
|
|
||||||
taskHtmlContent.element.classList.remove("line-through")
|
|
||||||
|
|
||||||
new_task_content.clear()
|
|
||||||
taskHtmlCheck.element.onclick = check_task
|
|
||||||
|
|
||||||
def add_task_event(e):
|
|
||||||
console.log("im in")
|
|
||||||
if (e.key == "Enter"):
|
|
||||||
add_task()
|
|
||||||
|
|
||||||
</py-script>
|
|
||||||
|
|
||||||
<main class="max-w-xs mx-auto mt-4">
|
<main class="max-w-xs mx-auto mt-4">
|
||||||
<section>
|
<section>
|
||||||
@@ -80,4 +44,5 @@ def add_task_event(e):
|
|||||||
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
38
pyscriptjs/examples/todo.py
Normal file
38
pyscriptjs/examples/todo.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from datetime import datetime as dt
|
||||||
|
from pyscript import Element
|
||||||
|
from js import console
|
||||||
|
|
||||||
|
tasks = []
|
||||||
|
|
||||||
|
# define the task template that will be use to render new templates to the page
|
||||||
|
task_template = Element("task-template").select('.task', from_content=True)
|
||||||
|
task_list = Element("list-tasks-container")
|
||||||
|
new_task_content = Element("new-task-content")
|
||||||
|
|
||||||
|
def add_task(*ags, **kws):
|
||||||
|
# create task
|
||||||
|
task_id = f"task-{len(tasks)}"
|
||||||
|
task = {"id": task_id, "content": new_task_content.element.value, "done": False, "created_at": dt.now()}
|
||||||
|
tasks.append(task)
|
||||||
|
|
||||||
|
# add the task element to the page as new node in the list by cloning from a template
|
||||||
|
taskHtml = task_template.clone(task_id, to=task_list)
|
||||||
|
taskHtmlContent = taskHtml.select('p')
|
||||||
|
taskHtmlContent.element.innerText = task['content']
|
||||||
|
taskHtmlCheck = taskHtml.select('input')
|
||||||
|
task_list.element.appendChild(taskHtml.element)
|
||||||
|
|
||||||
|
def check_task(evt=None):
|
||||||
|
task['done'] = not task['done']
|
||||||
|
if task['done']:
|
||||||
|
taskHtmlContent.element.classList.add("line-through")
|
||||||
|
else:
|
||||||
|
taskHtmlContent.element.classList.remove("line-through")
|
||||||
|
|
||||||
|
new_task_content.clear()
|
||||||
|
taskHtmlCheck.element.onclick = check_task
|
||||||
|
|
||||||
|
def add_task_event(e):
|
||||||
|
console.log("im in")
|
||||||
|
if (e.key == "Enter"):
|
||||||
|
add_task()
|
||||||
22
pyscriptjs/package-lock.json
generated
22
pyscriptjs/package-lock.json
generated
@@ -1211,6 +1211,12 @@
|
|||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"min-indent": {
|
"min-indent": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||||
@@ -1305,6 +1311,12 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"opener": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||||
|
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"opts": {
|
"opts": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
|
||||||
@@ -1581,6 +1593,16 @@
|
|||||||
"livereload": "^0.9.1"
|
"livereload": "^0.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rollup-plugin-serve": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-serve/-/rollup-plugin-serve-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-pYkSsuA0/psKqhhictkJw1c2klya5b+LlCvipWqI9OE1aG2M97mRumZCbBlry5CMEOzYBBgSDgd1694sNbmyIw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"mime": ">=2.4.6",
|
||||||
|
"opener": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"rollup-plugin-svelte": {
|
"rollup-plugin-svelte": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
"rollup-plugin-css-only": "^3.1.0",
|
"rollup-plugin-css-only": "^3.1.0",
|
||||||
"rollup-plugin-livereload": "^2.0.0",
|
"rollup-plugin-livereload": "^2.0.0",
|
||||||
|
"rollup-plugin-serve": "^1.1.0",
|
||||||
"rollup-plugin-svelte": "^7.0.0",
|
"rollup-plugin-svelte": "^7.0.0",
|
||||||
"rollup-plugin-terser": "^7.0.0",
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
"svelte": "^3.0.0",
|
"svelte": "^3.0.0",
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import { terser } from "rollup-plugin-terser";
|
|||||||
import sveltePreprocess from "svelte-preprocess";
|
import sveltePreprocess from "svelte-preprocess";
|
||||||
import typescript from "@rollup/plugin-typescript";
|
import typescript from "@rollup/plugin-typescript";
|
||||||
import css from "rollup-plugin-css-only";
|
import css from "rollup-plugin-css-only";
|
||||||
|
import serve from 'rollup-plugin-serve'
|
||||||
|
|
||||||
const production = !process.env.ROLLUP_WATCH;
|
const production = !process.env.ROLLUP_WATCH;
|
||||||
|
|
||||||
function serve() {
|
function serve_() {
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
function toExit() {
|
function toExit() {
|
||||||
@@ -40,7 +41,7 @@ export default {
|
|||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
format: "iife",
|
format: "iife",
|
||||||
name: "app",
|
name: "app",
|
||||||
file: "build/pyscript.js",
|
file: "examples/build/pyscript.js",
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
svelte({
|
svelte({
|
||||||
@@ -67,6 +68,10 @@ export default {
|
|||||||
!production && serve(),
|
!production && serve(),
|
||||||
!production && livereload("public"),
|
!production && livereload("public"),
|
||||||
production && terser(),
|
production && terser(),
|
||||||
|
serve({
|
||||||
|
port: 8080,
|
||||||
|
contentBase: 'examples'}
|
||||||
|
)
|
||||||
],
|
],
|
||||||
watch: {
|
watch: {
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
|
|||||||
@@ -49,9 +49,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now we call all post initializers AFTER we actually executed all page scripts
|
// now we call all post initializers AFTER we actually executed all page scripts
|
||||||
|
setTimeout(() => {
|
||||||
for (let initializer of $postInitializers){
|
for (let initializer of $postInitializers){
|
||||||
initializer();
|
initializer();
|
||||||
}
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleComponentsNavBar(evt){
|
function toggleComponentsNavBar(evt){
|
||||||
@@ -61,7 +64,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.19.0/full/pyodide.js" on:load={initializePyodide}></script>
|
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Tailwind />
|
<Tailwind />
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export class PyScript extends HTMLElement {
|
|||||||
btnConfig: HTMLElement;
|
btnConfig: HTMLElement;
|
||||||
btnRun: HTMLElement;
|
btnRun: HTMLElement;
|
||||||
editorOut: HTMLElement; //HTMLTextAreaElement;
|
editorOut: HTMLElement; //HTMLTextAreaElement;
|
||||||
|
source: string;
|
||||||
// editorState: EditorState;
|
// editorState: EditorState;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -205,6 +206,10 @@ export class PyScript extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('connected');
|
console.log('connected');
|
||||||
|
|
||||||
|
if (this.hasAttribute('src')) {
|
||||||
|
this.source = this.getAttribute('src');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addToOutput(s: string) {
|
addToOutput(s: string) {
|
||||||
@@ -212,8 +217,36 @@ export class PyScript extends HTMLElement {
|
|||||||
this.editorOut.hidden = false;
|
this.editorOut.hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadFromFile(s: string){
|
||||||
|
let pyodide = await pyodideReadyPromise;
|
||||||
|
let response = await fetch(s);
|
||||||
|
this.code = await response.text();
|
||||||
|
|
||||||
|
await pyodide.runPythonAsync(this.code);
|
||||||
|
await pyodide.runPythonAsync(`
|
||||||
|
from pyodide.http import pyfetch
|
||||||
|
from pyodide import eval_code
|
||||||
|
response = await pyfetch("`+s+`")
|
||||||
|
content = await response.bytes()
|
||||||
|
|
||||||
|
with open("todo.py", "wb") as f:
|
||||||
|
print(content)
|
||||||
|
f.write(content)
|
||||||
|
print("done writing")
|
||||||
|
`)
|
||||||
|
// let pkg = pyodide.pyimport("todo");
|
||||||
|
// pyodide.runPython(`
|
||||||
|
// import todo
|
||||||
|
// `)
|
||||||
|
// pkg.do_something();
|
||||||
|
}
|
||||||
|
|
||||||
async evaluate() {
|
async evaluate() {
|
||||||
|
|
||||||
console.log('evaluate');
|
console.log('evaluate');
|
||||||
|
if (this.source){
|
||||||
|
this.loadFromFile(this.source)
|
||||||
|
}else{
|
||||||
let pyodide = await pyodideReadyPromise;
|
let pyodide = await pyodideReadyPromise;
|
||||||
// debugger
|
// debugger
|
||||||
try {
|
try {
|
||||||
@@ -239,6 +272,7 @@ export class PyScript extends HTMLElement {
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
console.log('rendered');
|
console.log('rendered');
|
||||||
@@ -298,5 +332,5 @@ async function mountElements() {
|
|||||||
}
|
}
|
||||||
await pyodide.runPythonAsync(source);
|
await pyodide.runPythonAsync(source);
|
||||||
}
|
}
|
||||||
addPostInitializer(initHandlers);
|
|
||||||
addInitializer(mountElements);
|
addInitializer(mountElements);
|
||||||
|
addPostInitializer(initHandlers);
|
||||||
|
|||||||
@@ -98,17 +98,29 @@ pyscript = PyScript()
|
|||||||
|
|
||||||
let loadInterpreter = async function(): any {
|
let loadInterpreter = async function(): any {
|
||||||
/* @ts-ignore */
|
/* @ts-ignore */
|
||||||
|
console.log("creating pyodide runtime");
|
||||||
pyodide = await loadPyodide({
|
pyodide = await loadPyodide({
|
||||||
indexURL: "https://cdn.jsdelivr.net/pyodide/v0.19.0/full/",
|
|
||||||
stdout: console.log,
|
stdout: console.log,
|
||||||
stderr: console.log
|
stderr: console.log
|
||||||
});
|
});
|
||||||
|
|
||||||
// now that we loaded, add additional convenience fuctions
|
// now that we loaded, add additional convenience fuctions
|
||||||
|
console.log("loading micropip");
|
||||||
await pyodide.loadPackage("micropip");
|
await pyodide.loadPackage("micropip");
|
||||||
|
console.log('loading pyscript module');
|
||||||
|
await pyodide.runPythonAsync(`
|
||||||
|
from pyodide.http import pyfetch
|
||||||
|
response = await pyfetch("/build/pyscript.py")
|
||||||
|
with open("pyscript.py", "wb") as f:
|
||||||
|
content = await response.bytes()
|
||||||
|
print(content)
|
||||||
|
f.write(content)
|
||||||
|
`)
|
||||||
|
let pkg = pyodide.pyimport("pyscript");
|
||||||
|
|
||||||
|
console.log("creating additional definitions");
|
||||||
let output = pyodide.runPython(additional_definitions);
|
let output = pyodide.runPython(additional_definitions);
|
||||||
|
console.log("done setting up environment");
|
||||||
/* @ts-ignore */
|
/* @ts-ignore */
|
||||||
return pyodide;
|
return pyodide;
|
||||||
}
|
}
|
||||||
|
|||||||
90
pyscriptjs/src/pyscript.py
Normal file
90
pyscriptjs/src/pyscript.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
from js import document, setInterval, console
|
||||||
|
import asyncio
|
||||||
|
import io, base64
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
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}');
|
||||||
|
exec_id = exec_id or element.childElementCount + 1
|
||||||
|
element_id = child.id = f"{element_id}-{exec_id}";
|
||||||
|
element.appendChild(child);
|
||||||
|
|
||||||
|
if hasattr(value, "savefig"):
|
||||||
|
console.log(f"FIGURE: {value}")
|
||||||
|
buf = io.BytesIO()
|
||||||
|
value.savefig(buf, format='png')
|
||||||
|
buf.seek(0)
|
||||||
|
img_str = 'data:image/png;base64,' + base64.b64encode(buf.read()).decode('UTF-8')
|
||||||
|
document.getElementById(element_id).innerHTML = f'<div><img id="plt" src="{img_str}"/></div>'
|
||||||
|
elif hasattr(value, "startswith") and value.startswith("data:image"):
|
||||||
|
console.log(f"DATA/IMAGE: {value}")
|
||||||
|
document.getElementById(element_id).innerHTML = f'<div><img id="plt" src="{value}"/></div>'
|
||||||
|
else:
|
||||||
|
document.getElementById(element_id).innerHTML = repr(value);
|
||||||
|
console.log(f"ELSE: {append} ==> {element_id} --> {value}")
|
||||||
|
|
||||||
|
@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 element(self):
|
||||||
|
"""Return the dom element"""
|
||||||
|
if not self._element:
|
||||||
|
self._element = document.querySelector(f'#{self._id}');
|
||||||
|
return self._element
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
pyscript = PyScript()
|
||||||
Reference in New Issue
Block a user