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