For the past hour or so I have been working on a Python module that can run a compiled Elm 0.19 module that runs on Platform.worker
. It’s actually very easy to do and it uses less than 40 lines of Python code. I’m using this dukpy library to run the javascript (it’s not available on PyPI and there is a completely different package with the same name on PyPI).
This is the Python code:
import dukpy
import time
GLOBALS = '''
var console = {
warn: function(str) {},
log: function(str) {},
};
'''
def set_timeout(f, milliseconds):
time.sleep(milliseconds)
f()
class Elm:
def __init__(self, jscode, main_module, flags=''):
self.ctx = dukpy.Context()
self.ctx.eval(GLOBALS)
self.ctx.g.setTimeout = set_timeout
self.ctx.eval(jscode)
self.ctx.eval(f'var elm = Elm.{main_module}.init({flags});')
def subscribe(self, port_name):
def decor(f):
self.ctx.g.tempfunc = f
self.ctx.eval(f'elm.ports.{port_name}.subscribe(tempfunc);')
return f
return decor
def send(self, port_name, arg='null'):
self.ctx.eval(f'elm.ports.{port_name}.send({arg});')
As an example, I wrote a counter app that runs on Platform.worker
. It defines these ports:
port getNum : ( () -> msg ) -> Sub msg -- Immediately triggers gotNum
port increment : ( () -> msg ) -> Sub msg
port decrement : ( () -> msg ) -> Sub msg
port gotNum : Int -> Cmd msg -- Sends the current counter state to JavaScript
And this is how I run this from Python (after running elm make --output=counter.js
):
with open('counter.js', 'r') as f:
elm = Elm(f.read(), 'Main')
@elm.subscribe('gotNum')
def when_num_received(num):
print(f'Got current number: {num}')
elm.send('getNum')
elm.send('increment')
elm.send('getNum')
elm.send('increment')
elm.send('increment')
elm.send('getNum')
elm.send('decrement')
elm.send('getNum')
Output:
Got current number: 0
Got current number: 1
Got current number: 3
Got current number: 2