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>
<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>
<py-script>
from datetime import datetime as dt
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>
<body class="container">
<!-- <py-repl id="my-repl" auto-generate="true"> </py-repl> -->
<py-script src="/todo.py"> </py-script>
<main class="max-w-xs mx-auto mt-4">
<section>
@@ -80,4 +44,5 @@ def add_task_event(e):
</section>
</main>
</body>
</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"
}
},
"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": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -1305,6 +1311,12 @@
"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": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
@@ -1581,6 +1593,16 @@
"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": {
"version": "7.1.0",
"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-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",

View File

@@ -6,10 +6,11 @@ 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'
const production = !process.env.ROLLUP_WATCH;
function serve() {
function serve_() {
let server;
function toExit() {
@@ -40,7 +41,7 @@ export default {
sourcemap: true,
format: "iife",
name: "app",
file: "build/pyscript.js",
file: "examples/build/pyscript.js",
},
plugins: [
svelte({
@@ -67,6 +68,10 @@ export default {
!production && serve(),
!production && livereload("public"),
production && terser(),
serve({
port: 8080,
contentBase: 'examples'}
)
],
watch: {
clearScreen: false,

View File

@@ -49,9 +49,12 @@
}
// now we call all post initializers AFTER we actually executed all page scripts
for (let initializer of $postInitializers){
initializer();
}
setTimeout(() => {
for (let initializer of $postInitializers){
initializer();
}
}, 5000);
}
function toggleComponentsNavBar(evt){
@@ -61,7 +64,7 @@
</script>
<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>
<Tailwind />

View File

@@ -95,6 +95,7 @@ export class PyScript extends HTMLElement {
btnConfig: HTMLElement;
btnRun: HTMLElement;
editorOut: HTMLElement; //HTMLTextAreaElement;
source: string;
// editorState: EditorState;
constructor() {
@@ -205,6 +206,10 @@ export class PyScript extends HTMLElement {
}
console.log('connected');
if (this.hasAttribute('src')) {
this.source = this.getAttribute('src');
}
}
addToOutput(s: string) {
@@ -212,8 +217,36 @@ export class PyScript extends HTMLElement {
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() {
console.log('evaluate');
if (this.source){
this.loadFromFile(this.source)
}else{
let pyodide = await pyodideReadyPromise;
// debugger
try {
@@ -238,8 +271,9 @@ export class PyScript extends HTMLElement {
this.addToOutput(err);
console.log(err);
}
}
}
render(){
console.log('rendered');
@@ -298,5 +332,5 @@ async function mountElements() {
}
await pyodide.runPythonAsync(source);
}
addPostInitializer(initHandlers);
addInitializer(mountElements);
addPostInitializer(initHandlers);

View File

@@ -98,17 +98,29 @@ pyscript = PyScript()
let loadInterpreter = async function(): any {
/* @ts-ignore */
console.log("creating pyodide runtime");
pyodide = await loadPyodide({
indexURL: "https://cdn.jsdelivr.net/pyodide/v0.19.0/full/",
stdout: console.log,
stderr: console.log
});
// now that we loaded, add additional convenience fuctions
console.log("loading 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);
console.log("done setting up environment");
/* @ts-ignore */
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()