mirror of
https://github.com/pyscript/pyscript.git
synced 2022-05-01 19:47:48 +03:00
166 lines
4.9 KiB
Python
166 lines
4.9 KiB
Python
from js import document, setInterval, console
|
|
import micropip
|
|
import asyncio
|
|
import io, base64, sys
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
MIME_METHODS = {
|
|
'__repr__': 'text/plain',
|
|
'_repr_html_': 'text/html',
|
|
'_repr_markdown_': 'text/markdown',
|
|
'_repr_svg_': 'image/svg+xml',
|
|
'_repr_png_': 'image/png',
|
|
'repr_pdf_': 'application/pdf',
|
|
'_repr_jpeg_': 'image/jpeg',
|
|
'_repr_latex': 'text/latex',
|
|
'_repr_json_': 'application/json',
|
|
'_repr_javascript_': 'application/javascript',
|
|
'savefig': 'image/png'
|
|
}
|
|
|
|
def render_image(mime, value, meta):
|
|
data = f'{mime};base64,{value}'
|
|
attrs = ' '.join(['{k}="{v}"' for k, v in meta.items()])
|
|
return f'<img src="{data}" {attrs}</img>'
|
|
|
|
def identity(value, meta):
|
|
return value
|
|
|
|
|
|
MIME_RENDERERS = {
|
|
'text/plain': identity,
|
|
'text/html' : identity,
|
|
'image/png' : lambda value, meta: render_image('image/png', value, meta),
|
|
'image/jpeg': lambda value, meta: render_image('image/jpeg', value, meta),
|
|
'image/svg+xml': identity,
|
|
'application/json': identity,
|
|
'application/javascript': lambda value, meta: f'<script>{value}</script>'
|
|
}
|
|
|
|
|
|
def eval_formatter(obj, print_method):
|
|
"""
|
|
Evaluates a formatter method.
|
|
"""
|
|
if hasattr(obj, print_method):
|
|
if print_method == 'savefig':
|
|
buf = io.BytesIO()
|
|
obj.savefig(buf, format='png')
|
|
buf.seek(0)
|
|
return base64.b64encode(buf.read()).decode('utf-8')
|
|
return getattr(obj, print_method)()
|
|
if print_method == '_repr_mimebundle_':
|
|
return {}, {}
|
|
return None
|
|
|
|
|
|
def format_mime(obj):
|
|
"""
|
|
Formats object using _repr_x_ methods.
|
|
"""
|
|
format_dict, md_dict = eval_formatter(obj, '_repr_mimebundle_')
|
|
|
|
output, not_available = None, []
|
|
for method, mime_type in reversed(MIME_METHODS.items()):
|
|
if mime_type in format_dict:
|
|
output = format_dict[mime_type]
|
|
else:
|
|
output = eval_formatter(obj, method)
|
|
|
|
if output is None:
|
|
continue
|
|
elif mime_type not in MIME_RENDERERS:
|
|
not_available.append(mime_type)
|
|
continue
|
|
break
|
|
if output is None:
|
|
if not_available:
|
|
console.warning(f'Rendered object requested unavailable MIME renderers: {not_available}')
|
|
output = repr(output)
|
|
mime_type = 'text/plain'
|
|
elif isinstance(output, tuple):
|
|
output, meta = output
|
|
else:
|
|
meta = {}
|
|
return MIME_RENDERERS[mime_type](output, meta), mime_type
|
|
|
|
|
|
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);
|
|
|
|
element = document.getElementById(element_id)
|
|
html, mime_type = format_mime(value)
|
|
console.log(mime_type, html)
|
|
if mime_type == 'application/javascript':
|
|
scriptEl = document.createRange().createContextualFragment(html)
|
|
element.children = [scriptEl]
|
|
else:
|
|
element.innerHTML = html
|
|
|
|
@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)
|