Skip to content

metapages/metaframe-py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

87 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python metaframe (via pyiodide)

graph LR
    classDef mpColor fill:#87d0ff,stroke:#87d0ff,stroke-width:2px;
    subgraph metapage
    direction LR
        left1(upstream metaframe) -->|inputs| M(python code):::mpColor
        M --> |outputs| right1(downstream metaframe)
    end

Loading

Examples:

  • create matplotlib plots

  • inputs/outputs

  • Run python code entire in the browser, and connect to other metaframes

  • Edit python code directly in the frame

  • All code and configuration is stored in the URL

    • shareable and durable

Configuration + Inputs/Outputs

There are two (metaframe) modes (set in ⚙️ Options):

  1. ✅ Re-run python code on new inputs: Anytime new metaframe inputs arrive, restart the script. Any files in /outputs are copied and send as metaframe outputs. If the file suffix is recognized (e.g. .js, .json, .py) then it will be sent as a string (or directly as JSON if ending in .json), otherwise it will be send as an ArrayBuffer.
    • Inputs are copied as files
  2. ❌ Re-run python code on new inputs: Run the script once, the script is responsible for listening to inputs by calling the metaframe object (see examples below).
    • Inputs are not copied as files

The use case of copying inputs and outputs as files but *not re-running seems counterintuitive to me, let me know if you have this use case: https://github.com/metapages/metaframe-py/issues

Useful code snippets

Get the (current) metaframe inputs

from js import metaframe

inputs = metaframe.getInputs().as_object_map()

for key in inputs:
    print(key)
    print(inputs[key])
    # Do something with the value

Listen to new metaframe inputs

from pyodide.ffi import create_proxy
from js import metaframe

def onInputs(*args):
    print('got inputs in python:')
    inputs = args[0].as_object_map()
    for key in inputs:
      print(key)

onInputsProxy = create_proxy(onInputs) 
disposer = metaframe.onInputs(onInputsProxy)
# Call the disposer via the scriptUnload described next below

Unload/cleanup/dispose

When iterating/editing, the python script is repeatedly run in the pyiodide object. This can cause problems if proxied js <-> python objects are not properly disposed.

Before every new python script execution, (in javascript) if a window.scriptUnload script exists, it is called. This is where you put your cleanup:

from pyodide.ffi import create_proxy
from js import window

# Put this at the end of your script:
def scriptUnload(*args):
    print('unloading from python')
    # Call your cleanup code here
proxy_scriptUnload = create_proxy(scriptUnload)
window.scriptUnload = proxy_scriptUnload

Logging to the main div

Some convenience functions:

# log is the same as logStderr 
from js import log, logStdout, logStderr

# These functions only take a single string, no fancy formatting or multiple arguments
log("some string")
logStdout("some string")
logStderr("some error")

Advanced