1
0
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:
Fabio Pliger
2022-04-12 10:55:00 -05:00
committed by GitHub
9 changed files with 221 additions and 51 deletions

View File

@@ -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>

View 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()

View File

@@ -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",

View File

@@ -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",

View File

@@ -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,

View File

@@ -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
for (let initializer of $postInitializers){ setTimeout(() => {
initializer(); for (let initializer of $postInitializers){
} 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 />

View File

@@ -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 {
@@ -238,6 +271,7 @@ export class PyScript extends HTMLElement {
this.addToOutput(err); this.addToOutput(err);
console.log(err); console.log(err);
} }
}
} }
render(){ render(){
@@ -298,5 +332,5 @@ async function mountElements() {
} }
await pyodide.runPythonAsync(source); await pyodide.runPythonAsync(source);
} }
addPostInitializer(initHandlers);
addInitializer(mountElements); addInitializer(mountElements);
addPostInitializer(initHandlers);

View File

@@ -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;
} }

View 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()